Skip to content

Instantly share code, notes, and snippets.

@tilkinsc
Created September 8, 2017 07:02
Show Gist options
  • Save tilkinsc/d1a8a46853dea160dc86aa48618be6f9 to your computer and use it in GitHub Desktop.
Save tilkinsc/d1a8a46853dea160dc86aa48618be6f9 to your computer and use it in GitHub Desktop.
Loading a DDS file DXT1 DXT3 DXT5 opengl
GLuint texture_loadDDS(const char* path) {
FILE* f;
f = fopen(path, "rb");
if(f == 0)
return 0;
// make sure this is a dds
char filecode[4];
fread(&filecode, 1, 4, f);
if(strncmp(filecode, "DDS ", 4) != 0) {
fclose(f);
return 0;
}
unsigned char header[124];
fread(&header, 1, 124, f);
// different depending on byte arrangement/endianess, this is the windows version
unsigned int width = (header[8]) | (header[9] << 8) | (header[10] << 16) | (header[11] << 24);
unsigned int height = (header[12]) | (header[13] << 8) | (header[14] << 16) | (header[15] << 24);
unsigned int linearSize = (header[16]) | (header[17] << 8) | (header[18] << 16) | (header[19] << 24);
unsigned int mipMapCount = (header[24]) | (header[25] << 8) | (header[26] << 16) | (header[27] << 24);
// a 4-byte array of chars
char* fourCC = (char*)&(header[80]);
// 'the sum of all the mipmap's byte-size' is never greater than 'two times the biggest mipmap byte-size'
unsigned int buffer_size = (mipMapCount > 1 ? linearSize + linearSize : linearSize);
unsigned char* buffer = malloc(buffer_size * sizeof(unsigned char));
if(buffer == 0) {
// Handle OOM
return 0;
}
fread(buffer, 1, buffer_size, f);
fclose(f);
// decide byte format and block size from type
// probably should check if the EXT is available
unsigned int blockSize;
unsigned int format;
if(strncmp(fourCC, "DXT1", 4) == 0) {
format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
blockSize = 8;
} else if(strncmp(fourCC, "DXT3", 4) == 0) {
format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
blockSize = 16;
} else if(strncmp(fourCC, "DXT5", 4) == 0) {
format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
blockSize = 16;
} else {
// unhandled type
free(buffer);
return 0;
}
// glGenTextures() CAN return 0 on error, see docs - probably won't happen unless you are bad
GLuint tid;
glGenTextures(1, &tid);
if(tid == 0) {
free(buffer);
return 0;
}
glBindTexture(GL_TEXTURE_2D, tid);
// dds gives you the wrong amount of mipMaps, MAX_LEVEL 0 = 1 mipmap, MAX_LEVEL 1 = 2 mipmaps
// thusly just minus one - simple off-by-one array fix
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipMapCount-1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
unsigned int offset = 0;
for (unsigned int level=0; level<mipMapCount && (width || height); level++) {
// ignore mipmaps you will never see, save 4*4*4 + 2*2*4 + 1*1*4 data I guess
// if you don't at least ignore the 0*0 mipmaps, you will 0/2
// dds can have mipmaps that aren't rectangular, thusly 256*128 and 128*64 and 1*0 can be levels
if(width <= 4 || height <= 4) {
// -1 because this iteration of level is the next, which we don't use
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level-1);
break;
}
// the 3's aren't magic numbers
unsigned int size = ((width+3)/4) * ((height+3)/4) * blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, size, buffer + offset);
// offset is current byte offset from mip to map, as data is stored linearly
offset += size;
// assuming pow2 mipmaps
width = width/2;
height = height/2;
}
glBindTexture(GL_TEXTURE_2D, 0);
free(buffer);
return tid;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment