Browse Source

[WIP] Use DDS as internal texture format

Daniele Bartolini 11 năm trước cách đây
mục cha
commit
a5c2fd40af
2 tập tin đã thay đổi với 418 bổ sung145 xóa
  1. 308 133
      engine/resource/TextureResource.cpp
  2. 110 12
      engine/resource/TextureResource.h

+ 308 - 133
engine/resource/TextureResource.cpp

@@ -28,111 +28,107 @@ OTHER DEALINGS IN THE SOFTWARE.
 #include "Filesystem.h"
 #include "PixelFormat.h"
 #include "TextureResource.h"
+#include "ReaderWriter.h"
+#include "JSONParser.h"
 
 namespace crown
 {
 namespace texture_resource
 {
 
-struct TGAHeader
+struct ImageData
 {
-
-	char		id_length;			// 00h  Size of Image ID field
-	char		color_map_type;		// 01h  Color map type
-	char		image_type;			// 02h  Image type code
-	char		c_map_spec[5];		// 03h  Color map origin 05h Color map length 07h Depth of color map entries
-	uint16_t	x_offset;			// 08h  X origin of image
-	uint16_t	y_offset;			// 0Ah  Y origin of image
-	uint16_t	width;				// 0Ch  Width of image
-	uint16_t	height;				// 0Eh  Height of image
-	char		pixel_depth;     	// 10h  Image pixel size
-	char		image_descriptor;	// 11h  Image descriptor byte
+	uint32_t width;
+	uint32_t height;
+	PixelFormat::Enum format;
+	uint32_t num_mips;
+	char* data;
 };
 
-TGAHeader		m_tga_header;
-uint32_t		m_tga_channels;
-uint32_t		m_tga_size;
-
-TextureHeader	m_texture_header;
-size_t			m_texture_data_size = 0;
-uint8_t*		m_texture_data = NULL;
-
 //-----------------------------------------------------------------------------
-void swap_red_blue()
+void swap_red_blue(uint32_t width, uint32_t height, uint8_t channels, char* data)
 {
-	for (uint64_t i = 0; i < m_tga_size * m_tga_channels; i += m_tga_channels)
+	uint32_t i = 0;
+
+	for (uint32_t h = 0; h < height; h++)
 	{
-		m_texture_data[i + 0] ^= m_texture_data[i + 2];
-		m_texture_data[i + 2] ^= m_texture_data[i + 0];
-		m_texture_data[i + 0] ^= m_texture_data[i + 2];
+		for (uint32_t w = 0; w < width; w++)
+		{
+			const uint8_t tmp = data[i + 0];
+			data[i + 0] = data[i + 2];
+			data[i + 2] = tmp;
+
+			i += channels;
+		}
 	}
 }
 
 //-----------------------------------------------------------------------------
-void load_uncompressed(File* in_file)
+void read_tga_uncompressed(BinaryReader& br, uint32_t width, uint32_t height, uint8_t channels, ImageData& image)
 {
-	uint64_t size = m_tga_header.width * m_tga_header.height;
-
-	if (m_tga_channels == 2)
+	if (channels == 2)
 	{
-		int32_t j = 0;
+		uint32_t i = 0;
 
-		for (uint64_t i = 0; i < size * m_tga_channels; i++)
+		for (uint32_t h = 0; h < height; h++)
 		{
-			uint16_t pixel_data;
-			
-			in_file->read((char*)&pixel_data, sizeof(pixel_data));
-			
-			m_texture_data[j + 0] = (pixel_data & 0x7c) >> 10;
-			m_texture_data[j + 1] = (pixel_data & 0x3e) >> 5;
-			m_texture_data[j + 2] = (pixel_data & 0x1f);
-			
-			j += 3;
+			for (uint32_t w = 0; w < width; w++)
+			{
+				uint16_t data;
+				br.read(data);
+
+				image.data[i + 0] = (data & 0x7c) >> 10;
+				image.data[i + 1] = (data & 0x3e) >> 5;
+				image.data[i + 2] = (data & 0x1f);
+
+				i += 3;
+			}
 		}
 	}
 	else
 	{
-		in_file->read((char*)m_texture_data, (size_t)(size * m_tga_channels));
-
-		swap_red_blue();
+		br.read(image.data, width * height * channels);
+		swap_red_blue(width, height, channels, image.data);
 	}
 }
 
 //-----------------------------------------------------------------------------
-void load_compressed(File* in_file)
+void read_tga_compressed(BinaryReader& br, uint32_t width, uint32_t height, uint8_t channels, ImageData& image)
 {
 	uint8_t rle_id = 0;
 	uint32_t i = 0;
 	uint32_t colors_read = 0;
-	uint64_t size = m_tga_header.width * m_tga_header.height;
 
 	// Can't be more than 4 channels
 	uint8_t colors[4];
 
-	while (i < size)
+	while (i < width * height)
 	{
-		in_file->read((char*)&rle_id, sizeof(uint8_t));
+		br.read(rle_id);
 
 		// If MSB == 1
 		if (rle_id & 0x80)
 		{
 			rle_id -= 127;
 			
-			in_file->read((char*)&colors, m_tga_channels);
+			br.read(colors[0]);
+			br.read(colors[1]);
+			br.read(colors[2]);
+
+			if (channels == 4)
+				br.read(colors[3]);
 
 			while (rle_id)
 			{
-				m_texture_data[colors_read + 0] = colors[2];
-				m_texture_data[colors_read + 1] = colors[1];
-				m_texture_data[colors_read + 2] = colors[0];
+				image.data[colors_read + 0] = colors[2];
+				image.data[colors_read + 1] = colors[1];
+				image.data[colors_read + 2] = colors[0];
 
-				if (m_tga_channels == 4)
-				{
-					m_texture_data[colors_read + 3] = colors[3];
-				}
+				if (channels == 4)
+					image.data[colors_read + 3] = colors[3];
 
 				rle_id--;
-				colors_read += m_tga_channels;
+				colors_read += channels;
 				i++;
 			}
 		}
@@ -142,19 +138,22 @@ void load_compressed(File* in_file)
 
 			while (rle_id)
 			{
-				in_file->read((char*)colors, m_tga_channels);
+				br.read(colors[0]);
+				br.read(colors[1]);
+				br.read(colors[2]);
+
+				if (channels == 4)
+					br.read(colors[3]);
 				
-				m_texture_data[colors_read + 0] = colors[2];
-				m_texture_data[colors_read + 1] = colors[1];
-				m_texture_data[colors_read + 2] = colors[0];
+				image.data[colors_read + 0] = colors[2];
+				image.data[colors_read + 1] = colors[1];
+				image.data[colors_read + 2] = colors[0];
 
-				if (m_tga_channels == 4)
-				{
-					m_texture_data[colors_read + 3] = colors[3];
-				}
+				if (channels == 4)
+					image.data[colors_read + 3] = colors[3];
 
 				rle_id--;
-				colors_read += m_tga_channels;
+				colors_read += channels;
 				i++;
 			}
 		}
@@ -162,99 +161,275 @@ void load_compressed(File* in_file)
 }
 
 //-----------------------------------------------------------------------------
-void compile(Filesystem& fs, const char* resource_path, File* out_file)
+void parse_tga(BinaryReader& br, ImageData& image)
 {
-	File* in_file = fs.open(resource_path, FOM_READ);
+	uint8_t id;
+	br.read(id);
 
-	if (!in_file)
-	{
-		CE_LOGE("Unable to open file: %s", resource_path);
-		return;
-	}
+	uint8_t cmap_type;
+	br.read(cmap_type);
+
+	uint8_t image_type;
+	br.read(image_type);
+
+	uint8_t garbage;
+	for (uint32_t i = 0; i < 5; i++)
+		br.read(garbage);
+
+	uint16_t x_offt;
+	br.read(x_offt);
 
-	// Read the header
-	in_file->read((char*)(char*)&m_tga_header, sizeof(TGAHeader));
+	uint16_t y_offt;
+	br.read(y_offt);
+
+	uint16_t width;
+	br.read(width);
+
+	uint16_t height;
+	br.read(height);
+
+	uint8_t depth;
+	br.read(depth);
+
+	uint8_t desc;
+	br.read(desc);
 
 	// Skip TGA ID
-	in_file->skip(m_tga_header.id_length);
+	br.skip(id);
 
-	// Compute color channels
-	m_tga_channels = m_tga_header.pixel_depth / 8;
-	m_tga_size = m_tga_header.width * m_tga_header.height;
+	CE_ASSERT(image_type != 0, "TGA does not contain image data");
+	CE_ASSERT(image_type == 2 || image_type == 10, "TGA image format not supported");
 
-	m_texture_header.version = TEXTURE_VERSION;
-	m_texture_header.width = m_tga_header.width;
-	m_texture_header.height = m_tga_header.height;
+	const uint32_t channels = depth / 8;
 
-	// Select the appropriate pixel format and allocate
-	// resource data based on tga size and channels
-	switch (m_tga_channels)
-	{
-		case 2:
-		case 3:
-		{
-			m_texture_header.format = PixelFormat::RGB_8;
+	image.width = width;
+	image.height = height;
+	image.num_mips = 1;
 
-			m_texture_data_size = m_tga_size * 3;
-			m_texture_data = (uint8_t*)default_allocator().allocate(m_texture_data_size);
+	switch (channels)
+	{
+		case 2: image.format = PixelFormat::R8G8B8; break;
+		case 3: image.format = PixelFormat::R8G8B8; break;
+		case 4: image.format = PixelFormat::R8G8B8A8; break;
+		default: CE_FATAL("TGA channels not supported"); break;
+	}
 
-			break;
-		}
-		case 4:
-		{
-			m_texture_header.format = PixelFormat::RGBA_8;
+	image.data = (char*) default_allocator().allocate(Pixel::bytes_per_pixel(image.format) * width * height);
 
-			m_texture_data_size = m_tga_size * m_tga_channels;
-			m_texture_data = (uint8_t*)default_allocator().allocate(m_texture_data_size);
-			
-			break;
-		}
-		default:
-		{
-			CE_LOGE("Unable to determine TGA channels.");
-			return;
-		}
+	if (image_type == 2)
+	{
+		read_tga_uncompressed(br, width, height, channels, image);
+	}
+	else if (image_type == 10)
+	{
+		read_tga_compressed(br, width, height, channels, image);
 	}
 
-	// Determine image type (compressed/uncompressed) and call proper function to load TGA
-	switch (m_tga_header.image_type)
+	return;
+}
+
+static DdsPixelFormat PIXEL_FORMAT_TO_DDSPF[PixelFormat::COUNT] =
+{
+	{ DDPF_HEADERSIZE, DDS_FOURCC, DDFP_FOURCC_DXT1, 0, 0, 0, 0, 0 }, // DXT1
+	{ DDPF_HEADERSIZE, DDS_FOURCC, DDFP_FOURCC_DXT3, 0, 0, 0, 0, 0 }, // DXT3
+	{ DDPF_HEADERSIZE, DDS_FOURCC, DDFP_FOURCC_DXT5, 0, 0, 0, 0, 0 }, // DXT5
+
+	{ DDPF_HEADERSIZE, DDS_RGB,    0,                24, 0xFF0000, 0xFF00, 0xFF, 0}, // R8G8B8
+	{ DDPF_HEADERSIZE, DDS_RGBA,   0,                32, 0xFF000000, 0xFF0000, 0xFF00, 0xFF}, // R8G8B8A8
+
+	{ 0, 0, 0, 0, 0, 0, 0, 0}, // D16
+	{ 0, 0, 0, 0, 0, 0, 0, 0}, // D24
+	{ 0, 0, 0, 0, 0, 0, 0, 0}, // D32
+	{ 0, 0, 0, 0, 0, 0, 0, 0}, // D24S8
+};
+
+//-----------------------------------------------------------------------------
+void parse_dds(BinaryReader& br, ImageData& image)
+{
+	// Read header
+	uint32_t magic;
+	br.read(magic);
+	CE_ASSERT(magic == DDSD_MAGIC, "DDS bad magic number");
+
+	uint32_t hsize;
+	br.read(hsize);
+	CE_ASSERT(hsize == DDSD_HEADERSIZE, "DDS bas header size");
+
+	uint32_t flags;
+	br.read(flags);
+	CE_ASSERT(flags & (DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT), "DDS bad header flags");
+
+	uint32_t height;
+	br.read(height);
+
+	uint32_t width;
+	br.read(width);
+
+	uint32_t pitch;
+	br.read(pitch);
+
+	uint32_t depth;
+	br.read(depth);
+
+	uint32_t num_mips;
+	br.read(num_mips);
+
+	// Skip reserved bits
+	br.skip(sizeof(uint32_t) * 11);
+
+	// Read pixel format
+	uint32_t pf_hsize;
+	br.read(pf_hsize);
+	CE_ASSERT(pf_hsize == DDPF_HEADERSIZE, "DDS bad pf header size");
+
+	uint32_t pf_flags;
+	br.read(pf_flags);
+
+	uint32_t pf_fourcc;
+	br.read(pf_fourcc);
+
+	uint32_t pf_bitcount;
+	br.read(pf_bitcount);
+
+	uint32_t pf_rmask;
+	br.read(pf_rmask);
+
+	uint32_t pf_gmask;
+	br.read(pf_gmask);
+
+	uint32_t pf_bmask;
+	br.read(pf_bmask);
+
+	uint32_t pf_amask;
+	br.read(pf_amask);
+
+	uint32_t caps;
+	br.read(caps);
+	CE_ASSERT((caps & DDSCAPS_TEXTURE), "DDS bad caps");
+
+	uint32_t caps2;
+	br.read(caps2);
+
+	uint32_t caps3;
+	br.read(caps3);
+
+	uint32_t caps4;
+	br.read(caps4);
+
+	uint32_t reserved2;
+	br.read(reserved2);
+
+	image.width = width;
+	image.height = height;
+	image.num_mips = (flags & DDSD_MIPMAPCOUNT) ? num_mips : 1;
+	image.format = PixelFormat::COUNT;
+
+	DdsPixelFormat ddspf;
+	ddspf.size = pf_hsize;
+	ddspf.flags = pf_flags;
+	ddspf.fourcc = pf_fourcc;
+	ddspf.bitcount = pf_bitcount;
+	ddspf.rmask = pf_rmask;
+	ddspf.gmask = pf_gmask;
+	ddspf.bmask = pf_bmask;
+	ddspf.amask = pf_amask;
+
+	for (uint32_t i = 0; i < PixelFormat::COUNT; i++)
 	{
-		case 0:
+		if (memcmp(&ddspf, &PIXEL_FORMAT_TO_DDSPF[(PixelFormat::Enum) i], sizeof(DdsPixelFormat)) == 0)
 		{
-			CE_LOGE("The file does not contain image data: %s", resource_path);
-			return;
-		}
-		case 2:
-		{
-			load_uncompressed(in_file);
-			break;
+			image.format = (PixelFormat::Enum) i;
 		}
+	}
 
-		case 10:
-		{
-			load_compressed(in_file);
-			break;
-		}
+	CE_ASSERT(image.format != PixelFormat::COUNT, "DDS pixel format not supported");
+}
 
-		default:
-		{
-			CE_LOGE("Image type not supported.");
-			return;
-		}
+//-----------------------------------------------------------------------------
+void write_dds(BinaryWriter& bw, const ImageData& image)
+{
+	bw.write(DDSD_MAGIC);
+
+	// Header
+	bw.write(DDSD_HEADERSIZE); // dwSize
+	bw.write(DDS_HEADER_FLAGS_TEXTURE | DDSD_PITCH | DDSD_MIPMAPCOUNT); // dwFlags
+	bw.write(image.height); // dwHeight
+	bw.write(image.width); // dwWidth
+	const uint32_t pitch = uint32_t(( image.width * 32 + 7 ) / 8);
+	bw.write(pitch); // dwPitchOrLinearSize
+	bw.write(DDSD_UNUSED); // dwDepth
+	bw.write(image.num_mips); // dwMipMapCount;
+
+	for (uint32_t i = 0; i < 11; i++)
+		bw.write(DDSD_UNUSED); // dwReserved1[11];
+
+	// PixelFormat
+	bw.write(DDPF_HEADERSIZE); // dwSize;
+	const uint32_t pf = DDPF_RGB | (image.format == PixelFormat::R8G8B8A8 ? DDPF_ALPHAPIXELS : 0);
+	bw.write(pf); // dwFlags;
+	bw.write(DDSD_UNUSED); // dwFourCC;
+	bw.write(uint32_t(image.format == PixelFormat::R8G8B8A8 ? 32 : 24)); // dwRGBBitCount;
+	bw.write(uint32_t(0x000000FF)); // dwRBitMask;
+	bw.write(uint32_t(0x0000FF00)); // dwGBitMask;
+	bw.write(uint32_t(0x00FF0000)); // dwBBitMask;
+	bw.write(uint32_t(0xFF000000)); // dwABitMask;
+
+	bw.write(DDSCAPS_TEXTURE); // dwCaps;
+	bw.write(DDSD_UNUSED); // dwCaps2;
+	bw.write(DDSD_UNUSED); // dwCaps3;
+	bw.write(DDSD_UNUSED); // dwCaps4;
+	bw.write(DDSD_UNUSED); // dwReserved2;
+
+	const char* data = image.data;
+	for (uint32_t i = 0; i < image.height; i++)
+	{
+		bw.write((const void*) data, pitch);
+		data += pitch;
 	}
+}
 
+//-----------------------------------------------------------------------------
+void compile(Filesystem& fs, const char* resource_path, File* out_file)
+{
+	File* in_file = fs.open(resource_path, FOM_READ);
+	JSONParser json(*in_file);
 	fs.close(in_file);
 
-	out_file->write((char*)&m_texture_header, sizeof(TextureHeader));
-	out_file->write((char*)m_texture_data, m_texture_data_size);
+	// Read source file
+	JSONElement root = json.root();
+	DynamicString name;
+	root.key("source").to_string(name);
+
+	File* source = fs.open(name.c_str(), FOM_READ);
+	BinaryReader br(*source);
+	ImageData image;
 
-	if (m_texture_data)
+	if (name.ends_with(".tga"))
+	{
+		parse_tga(br, image);
+	}
+	else if (name.ends_with(".dds"))
+	{
+		// parse_dds(br, image);
+		CE_FATAL(".dds not supported");
+	}
+	else
 	{
-		default_allocator().deallocate(m_texture_data);
-		m_texture_data_size = 0;
-		m_texture_data = NULL;
+		CE_FATAL("Source image not supported");
 	}
+
+	fs.close(source);
+
+	BinaryWriter bw(*out_file);
+	
+	// Write texture header
+	bw.write(uint32_t(0x00000000));
+
+	// Write DDS
+	write_dds(bw, image);
+
+	default_allocator().deallocate(image.data);
 }
 
 } // namespace texture_resource
-} // namespace crown
+} // namespace crown

+ 110 - 12
engine/resource/TextureResource.h

@@ -38,18 +38,100 @@ OTHER DEALINGS IN THE SOFTWARE.
 namespace crown
 {
 
-// Bump the version whenever a change in the header is made
-const uint32_t TEXTURE_VERSION = 1;
+#define FOURCC(a, b, c, d)			uint32_t(a | (b << 8) | (c << 16) | (d << 24))
+
+#define DDSD_MAGIC 					FOURCC('D', 'D', 'S', ' ')
+#define DDSD_HEADERSIZE				uint32_t(124)
+#define DDSD_UNUSED					uint32_t(0x00000000)
+#define DDSD_CAPS					uint32_t(0x00000001) // Required in every .dds file.
+#define DDSD_HEIGHT					uint32_t(0x00000002) // Required in every .dds file.
+#define DDSD_WIDTH					uint32_t(0x00000004) // Required in every .dds file.
+#define DDSD_PITCH					uint32_t(0x00000008) // Required when pitch is provided for an uncompressed texture.
+#define DDSD_PIXELFORMAT			uint32_t(0x00001000) // Required in every .dds file.
+#define DDSD_MIPMAPCOUNT			uint32_t(0x00020000) // Required in a mipmapped texture.
+#define DDSD_LINEARSIZE				uint32_t(0x00080000) // Required when pitch is provided for a compressed texture.
+#define DDSD_DEPTH					uint32_t(0x00800000) // Required in a depth texture.
+
+#define DDS_HEADER_FLAGS_TEXTURE	uint32_t(DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT)
+#define DDS_HEADER_FLAGS_MIPMAP		uint32_t(DDSD_MIPMAPCOUNT)
+#define DDS_HEADER_FLAGS_VOLUME		uint32_t(DDSD_DEPTH)
+#define DDS_HEADER_FLAGS_PITCH		uint32_t(DDSD_PITCH)
+#define DDS_HEADER_FLAGS_LINEARSIZE	uint32_t(DDSD_LINEARSIZE)
+
+#define DDSCAPS_COMPLEX				uint32_t(0x00000008) // Optional; must be used on any file that contains more than one surface (a mipmap, a cubic environment map, or mipmapped volume texture).
+#define DDSCAPS_MIPMAP				uint32_t(0x00400000) // Optional; should be used for a mipmap.
+#define DDSCAPS_TEXTURE				uint32_t(0x00001000) // Required
+
+#define DDSCAPS2_CUBEMAP			uint32_t(0x00000200) // Required for a cube map.
+#define DDSCAPS2_CUBEMAP_POSITIVEX	uint32_t(0x00000400) // Required when these surfaces are stored in a cube map.
+#define DDSCAPS2_CUBEMAP_NEGATIVEX	uint32_t(0x00000800) // Required when these surfaces are stored in a cube map.
+#define DDSCAPS2_CUBEMAP_POSITIVEY	uint32_t(0x00001000) // Required when these surfaces are stored in a cube map.
+#define DDSCAPS2_CUBEMAP_NEGATIVEY	uint32_t(0x00002000) // Required when these surfaces are stored in a cube map.
+#define DDSCAPS2_CUBEMAP_POSITIVEZ	uint32_t(0x00004000) // Required when these surfaces are stored in a cube map.
+#define DDSCAPS2_CUBEMAP_NEGATIVEZ	uint32_t(0x00008000) // Required when these surfaces are stored in a cube map.
+#define DDSCAPS2_VOLUME				uint32_t(0x00200000) // Required for a volume texture.
+
+#define DDPF_HEADERSIZE				uint32_t(32)
+#define DDPF_ALPHAPIXELS			uint32_t(0x00000001) // Texture contains alpha data; dwRGBAlphaBitMask contains valid data.
+#define DDPF_ALPHA					uint32_t(0x00000002) // Used in some older DDS files for alpha channel only uncompressed data (dwRGBBitCount contains the alpha channel bitcount; dwABitMask contains valid data)
+#define DDPF_FOURCC					uint32_t(0x00000004) // Texture contains compressed RGB data; dwFourCC contains valid data.
+#define DDPF_RGB					uint32_t(0x00000040) // Texture contains uncompressed RGB data; dwRGBBitCount and the RGB masks (dwRBitMask, dwGBitMask, dwBBitMask) contain valid data.
+#define DDPF_YUV					uint32_t(0x00000200) // Used in some older DDS files for YUV uncompressed data (dwRGBBitCount contains the YUV bit count; dwRBitMask contains the Y mask, dwGBitMask contains the U mask, dwBBitMask contains the V mask)
+#define DDPF_LUMINANCE				uint32_t(0x00020000) // Used in some older DDS files for single channel color uncompressed data (dwRGBBitCount contains the luminance channel bit count; dwRBitMask contains the channel mask). Can be combined with DDPF_ALPHAPIXELS for a two channel DDS file.
+
+#define DDS_FOURCC					uint32_t(DDPF_FOURCC)
+#define DDS_RGB						uint32_t(DDPF_RGB)
+#define DDS_RGBA					uint32_t(DDPF_RGB | DDPF_ALPHAPIXELS)
+#define DDS_LUMINANCE				uint32_t(DDPF_LUMINANCE)
+#define DDS_LUMINANCEA				uint32_t(DDPF_LUMINANCE | DDPF_ALPHAPIXELS)
+#define DDS_ALPHA					uint32_t(DDPF_ALPHA)
+
+#define DDFP_FOURCC_DXT1			FOURCC('D', 'X', 'T', '1')
+#define DDFP_FOURCC_DXT2			FOURCC('D', 'X', 'T', '2')
+#define DDFP_FOURCC_DXT3			FOURCC('D', 'X', 'T', '3')
+#define DDFP_FOURCC_DXT4			FOURCC('D', 'X', 'T', '4')
+#define DDFP_FOURCC_DXT5			FOURCC('D', 'X', 'T', '5')
+#define DDFP_FOURCC_DX10			FOURCC('D', 'X', '1', '0')
+
+struct DdsPixelFormat
+{
+	uint32_t size;
+	uint32_t flags;
+	uint32_t fourcc;
+	uint32_t bitcount;
+	uint32_t rmask;
+	uint32_t gmask;
+	uint32_t bmask;
+	uint32_t amask;
+};
+
+struct DdsHeader
+{
+	uint32_t		magic;
+	uint32_t        size;
+	uint32_t        flags;
+	uint32_t        height;
+	uint32_t        width;
+	uint32_t        pitch_or_linear_size;
+	uint32_t        depth;
+	uint32_t        num_mips;
+	uint32_t        reserved[11];
+	DdsPixelFormat  ddspf;
+	uint32_t        caps;
+	uint32_t        caps2;
+	uint32_t        caps3;
+	uint32_t        caps4;
+	uint32_t        reserved2;
+};
 
 struct TextureHeader
 {
-	TextureId	id;
-	uint32_t	version;	// Texture file version
-	uint32_t	format;		// Format of the pixels
-	uint32_t	width;		// Width in pixels
-	uint32_t	height;		// Height in pixels
+	TextureId id;
 };
 
+#define DDS_HEADER_OFFSET	uint32_t(sizeof(TextureHeader))
+#define DDS_DATA_OFFSET		uint32_t(DDS_HEADER_OFFSET + DDSD_HEADERSIZE)
+
 struct TextureResource
 {
 	//-----------------------------------------------------------------------------
@@ -72,7 +154,7 @@ struct TextureResource
 		TextureResource* t = (TextureResource*) resource;
 		TextureHeader* h = (TextureHeader*) t;
 
-		h->id = device()->renderer()->create_texture(t->width(), t->height(), t->format(), t->data());
+		h->id = device()->renderer()->create_texture(t->width(), t->height(), t->num_mipmaps(), t->format(), t->data());
 	}
 
 	//-----------------------------------------------------------------------------
@@ -93,22 +175,38 @@ struct TextureResource
 
 	PixelFormat::Enum format() const
 	{
-		return (PixelFormat::Enum) ((TextureHeader*) this)->format;
+		const DdsPixelFormat& ddspf = ((DdsHeader*) (((char*) this) + DDS_HEADER_OFFSET))->ddspf;
+
+		const uint32_t fmt = ddspf.flags & DDPF_FOURCC ? ddspf.fourcc : ddspf.flags;
+		switch (fmt)
+		{
+			case DDFP_FOURCC_DXT1: return PixelFormat::DXT1; break;
+			case DDFP_FOURCC_DXT3: return PixelFormat::DXT3; break;
+			case DDFP_FOURCC_DXT5: return PixelFormat::DXT5; break;
+			case DDS_RGB: return PixelFormat::R8G8B8; break;
+			case DDS_RGBA: return PixelFormat::R8G8B8A8; break;
+			default: CE_FATAL("Unknown pixel format"); return PixelFormat::COUNT;
+		}
 	}
 
 	uint32_t width() const
 	{
-		return ((TextureHeader*) this)->width;
+		return ((DdsHeader*) (((char*) this) + DDS_HEADER_OFFSET))->width;
 	}
 
 	uint32_t height() const
 	{
-		return ((TextureHeader*) this)->height;
+		return ((DdsHeader*) (((char*) this) + DDS_HEADER_OFFSET))->height;
+	}
+
+	uint8_t num_mipmaps() const
+	{
+		return (uint8_t) ((DdsHeader*) (((char*) this) + DDS_HEADER_OFFSET))->num_mips;
 	}
 
 	const char* data() const
 	{
-		return (char*)this + sizeof(TextureHeader);
+		return (char*) this + DDS_DATA_OFFSET;
 	}
 
 	TextureId texture() const