Thanks to the Swizzled DXT5 technique (injection of red channel into alpha channel and use of DXT5 compression), we get
a normal-map that can be used in a wide range of situations and particularly in those where the specular highlights are small or
their quality is not important. In all other cases where the normal-map compression must give good results, the use of ATI's 3Dc
compression format is inevitable. The only drawback of this format is that only AT Radeon X800 and up are 3Dc-ready. So for more realism
and even if graphics controllers embark more and more memory, use compression, it's worth while! (see
S3TC Comparative Table for compression ratio).
The O3TC format is produced by the DXTViewer and is really simple to load. You will find below the O3TC file structure,
a portion of code that shows you how to load compressed textures and a pure Win32 / OpenGL demo
(complete Visual C++ 6.0 project) is provided. It shows how to load a texture in the S3TC/O3TC format (texture dims are 2048x2048, so I hope
you have a decent graphics controller since I did not add all hardware checks...) and how to setup the
texture trilinear filtering. It's sheer bliss!
// Magic number: Must be O3TC.
// Must be filled with sizeof(O3TC_Header).
// Version. Currently: 0x00000001.
// Must be filled with sizeof(O3TC_Chunk_Header):
// The size of the data chunk that follows this one.
// Pixel format:
// - O3_TC_RGB_S3TC_DXT1 = 1
// - O3_TC_RGBA_S3TC_DXT5 = 4
// - O3_TC_ATI3DC_ATI2N = 16
// Texture width.
// Texture height.
// Texture depth.
// Number of mipmaps.
// The texture name.
// The texture id.
// raw array of BYTE: this represents the
// real compressed data.
The first 4 bytes (data_size) give the total size of the data (headers included) held in the O3TC file:
o3tc_data_size = file_size - 4. These first four bytes can be skipped. Next, the file reading is quite simple:
1 - O3TC_Header reading in order to check the magic_number and file version.
2 - O3TC_Chunk_Header reading in order to retrieve texture dimensions (width and height), number of mipmaps
and compression type (DXT1 ou DXT5).
Direct reading of compressed data chunk. The size of this chunk is given by O3TC_Chunk_Header.size.
Next, this chunk has to be loaded into the graphics memory using the glCompressedTexImage2D() function.
Normally, this chunk is identical to that held in a DDS file.
Here is, extracted from the OpenGL demo (see void cDemo::setQuadTexture()), the piece of code that loads the compressed data chunk:
offset = 0;
size = 0;
for( mip=0; mip<=numMipMaps && (width || height); mip++ )
if (width == 0)
width = 1;
if (height == 0)
height = 1;
blockSize = 16;
internal_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
blockSize = 8;
internal_format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
size = ((width+3)/4)*((height+3)/4) * blockSize;
pCompressedPixmap + offset);
offset += size;
width >>= 1;
height >>= 1;