Loading DDS textures in OpenGL, Black Texture Showing

Started by
2 comments, last by swiftcoder 11 years, 3 months ago

I am loading DDS textures with the code below, and it works fine for square textures power of two textures, but it will not load textures that are 256*128 for example. I am not sure why that is, it should work as far as I can see. Any suggestions on this?


GLuint ImageUtils::loadDDS(const char * filename)
{
	Logger::getInstance()->write(StringUtils::format("Loading DDS Image %s",filename));

	unsigned char header[124];
 
	FILE *fp;
 
	/* try to open the file */
	fp = fopen(filename, "rb");
	if (fp == NULL)
	{
		Logger::getInstance()->write("Failed to load Image: could not open the file");
		return 0;
	}
 
	/* verify the type of file */
	char filecode[4];
	fread(filecode, 1, 4, fp);
	if (strncmp(filecode, "DDS ", 4) != 0) 
	{
		fclose(fp);
		Logger::getInstance()->write("Failed to load Image: not a direct draw surface file");
		return 0;
	}
 
	/* get the surface desc */
	fread(&header, 124, 1, fp); 
 
	unsigned int height = *(unsigned int*)&(header[8 ]);
	unsigned int width = *(unsigned int*)&(header[12]);
	unsigned int linearSize = *(unsigned int*)&(header[16]);
	unsigned int mipMapCount = *(unsigned int*)&(header[24]);
	unsigned int fourCC = *(unsigned int*)&(header[80]);

	unsigned char * buffer;
	unsigned int bufsize;

	/* how big is it going to be including all mipmaps? */
	bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;
	buffer = (unsigned char*)malloc(bufsize * sizeof(unsigned char));
	fread(buffer, 1, bufsize, fp);

	/* close the file pointer */
	fclose(fp);

	unsigned int components = (fourCC == FOURCC_DXT1) ? 3 : 4;
	unsigned int format;
	switch(fourCC)
	{
		case FOURCC_DXT1:
			format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
			break;
		case FOURCC_DXT3:
			format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
			break;
		case FOURCC_DXT5:
			format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
			break;
		default:
			free(buffer);
			Logger::getInstance()->write("Failed to load Image: dds file format not supported (supported formats: DXT1, DXT3, DXT5)");
			return 0;
	}

	// Create one OpenGL texture
	GLuint textureID;
	glGenTextures(1, &textureID);
 
	// "Bind" the newly created texture : all future texture functions will modify this texture
	glBindTexture(GL_TEXTURE_2D, textureID);
	glPixelStorei(GL_UNPACK_ALIGNMENT,1);  

	unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
	unsigned int offset = 0;
 
	/* load the mipmaps */
	for (unsigned int level = 0; level < mipMapCount && (width || height); ++level)
	{
		unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
		glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 
			0, size, buffer + offset);
 
		offset += size;
		width  /= 2;
		height /= 2;
	}

	free(buffer); 

	//Unbind the texture
	glBindTexture(GL_TEXTURE_2D, 0);

	Logger::getInstance()->write(StringUtils::format("Loaded DDS Image %s",filename));

	return textureID;
}

Advertisement

When you reach the last few mip-maps, one of width or height will reach zero before the other reaches one (and thus the mipmap will have zero dimension and be rendered as black).

You should change:


width  /= 2;
height /= 2;

To be something like this:


width = max(width/2, 1);
height = max(height/2, 1);

To ensure that neither dimension ever becomes zero.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Thank you

I forgot to mention that you might actually want to not produce those last few mipmaps, rather than clamping their size.

When you reach the last few mipmaps of a square texture, it looks like this: 8x8, 4x4, 2x2, 1x1. For a rectangular texture, it looks like this: 8x4, 4x2, 2x1, 1x1. There probably isn't a whole lot of value to having both a 2x1 and a 1x1 mipmap around - in which case you can just exit the loop as soon as either dimension reaches 1.

(on the other hand, these higher mipmaps don't really consume any memory - at most a few bytes, so up to you)

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

This topic is closed to new replies.

Advertisement