Objects won't render when Texture Compression + Mipmapping is Enabled
- by felipedrl
I'm optimizing my game and I've just implemented compressed (DXTn) texture loading in OpenGL. I've worked my way removing bugs but I can't figure out this one: objects w/ DXTn + mipmapped textures are not being rendered. It's not like they are appearing with a flat color, they just don't appear at all. DXTn textured objs render and mipmapped non-compressed textures render just fine.
The texture in question is 256x256 I generate the mips all the way down 4x4, i.e 1 block. I've checked on gDebugger and it display all the levels (7) just fine. I'm using GL_LINEAR_MIPMAP_NEAREST for min filter and GL_LINEAR for mag one.
The texture is being compressed and mipmaps being created offline with Paint.NET tool using super sampling method. (I also tried bilinear just in case)
Source follow:
[SNIPPET 1: Loading DDS into sys memory + Initializing Object]
// Read header
DDSHeader header;
file.read(reinterpret_cast<char*>(&header), sizeof(DDSHeader));
uint pos = static_cast<uint>(file.tellg());
file.seekg(0, std::ios_base::end);
uint dataSizeInBytes = static_cast<uint>(file.tellg()) - pos;
file.seekg(pos, std::ios_base::beg);
// Read file data
mData = new unsigned char[dataSizeInBytes];
file.read(reinterpret_cast<char*>(mData), dataSizeInBytes);
file.close();
mMipmapCount = header.mipmapcount;
mHeight = header.height;
mWidth = header.width;
mCompressionType = header.pf.fourCC;
// Only support files divisible by 4 (for compression blocks algorithms)
massert(mWidth % 4 == 0 && mHeight % 4 == 0);
massert(mCompressionType == NO_COMPRESSION || mCompressionType == COMPRESSION_DXT1 || mCompressionType == COMPRESSION_DXT3 ||
mCompressionType == COMPRESSION_DXT5);
// Allow textures up to 65536x65536
massert(header.mipmapcount <= MAX_MIPMAP_LEVELS);
mTextureFilter = TextureFilter::LINEAR;
if (mMipmapCount > 0)
{
mMipmapFilter = MipmapFilter::NEAREST;
}
else
{
mMipmapFilter = MipmapFilter::NO_MIPMAP;
}
mBitsPerPixel = header.pf.bitcount;
if (mCompressionType == NO_COMPRESSION)
{
if (header.pf.flags & DDPF_ALPHAPIXELS)
{
// The only format supported w/ alpha is A8R8G8B8
massert(header.pf.amask == 0xFF000000 && header.pf.rmask == 0xFF0000 &&
header.pf.gmask == 0xFF00 && header.pf.bmask == 0xFF);
mInternalFormat = GL_RGBA8;
mFormat = GL_BGRA;
mDataType = GL_UNSIGNED_BYTE;
}
else
{
massert(header.pf.rmask == 0xFF0000 && header.pf.gmask == 0xFF00 && header.pf.bmask == 0xFF);
mInternalFormat = GL_RGB8;
mFormat = GL_BGR;
mDataType = GL_UNSIGNED_BYTE;
}
}
else
{
uint blockSizeInBytes = 16;
switch (mCompressionType)
{
case COMPRESSION_DXT1:
blockSizeInBytes = 8;
if (header.pf.flags & DDPF_ALPHAPIXELS)
{
mInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
}
else
{
mInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
}
break;
case COMPRESSION_DXT3:
mInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
case COMPRESSION_DXT5:
mInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
// Not Supported (DXT2, DXT4 or any compression format)
massert(false);
}
}
[SNIPPET 2: Uploading into video memory]
massert(mData != NULL);
glGenTextures(1, &mHandle);
massert(mHandle!=0);
glBindTexture(GL_TEXTURE_2D, mHandle);
commitFiltering();
uint offset = 0;
Renderer* renderer = Renderer::getInstance();
switch (mInternalFormat)
{
case GL_RGB:
case GL_RGBA:
case GL_RGB8:
case GL_RGBA8:
for (uint i = 0; i < mMipmapCount + 1; ++i)
{
uint width = std::max(1U, mWidth >> i);
uint height = std::max(1U, mHeight >> i);
glTexImage2D(GL_TEXTURE_2D, i, mInternalFormat, width, height, mHasBorder, mFormat, mDataType, &mData[offset]);
offset += width * height * (mBitsPerPixel / 8);
}
break;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
{
uint blockSize = 16;
if (mInternalFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT || mInternalFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
{
blockSize = 8;
}
uint width = mWidth;
uint height = mHeight;
for (uint i = 0; i < mMipmapCount + 1; ++i)
{
uint nBlocks = ((width + 3) / 4) * ((height + 3) / 4);
// Only POT textures allowed for mipmapping
massert(width % 4 == 0 && height % 4 == 0);
glCompressedTexImage2D(GL_TEXTURE_2D, i, mInternalFormat, width, height, mHasBorder, nBlocks * blockSize, &mData[offset]);
offset += nBlocks * blockSize;
if (width <= 4 && height <= 4)
{
break;
}
width = std::max(4U, width / 2);
height = std::max(4U, height / 2);
}
break;
}
default:
// Not Supported
massert(false);
}
Also I don't understand the "+3" in the block size computation but looking for a solution for my problema I've encountered people defining it as that. I guess it won't make a differente for POT textures but I put just in case.
Thanks.