DDS Support

Discuss about anything related to the Irrlicht Engine, or read announcements about any significant features or usage changes.
Stalker
Posts: 1
Joined: Fri Nov 11, 2005 4:32 pm

DDS Support

Post by Stalker »

Will it be possible to see support for DDS textures in future releases ? DDS is way superior to, for example, TGAs so I think it will be wise to have support for them.
dlangdev
Posts: 1324
Joined: Tue Aug 07, 2007 7:28 pm
Location: Beaverton OR
Contact:

Post by dlangdev »

Can someone post a link to the DDS file spec.

That will come in handy when someone buckles down and writes a DDS reader.

Thanks.
Image
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Eugh... DDS files...

How are they superior? If they're uncompressed then they're huge and if they're compressed then it's a very lossy format in my experience.

What's wrong with PNG or lossless JPG?

The only advantage i can see of DDS really is the ability to have mipmaps in the texture file but then you end up with larger files and irrlicht will make the mipmaps for you anyway so it's pretty pointless...
Image Image Image
dlangdev
Posts: 1324
Joined: Tue Aug 07, 2007 7:28 pm
Location: Beaverton OR
Contact:

Post by dlangdev »

Actually, I need a DDS reader for my personal project currently under development. So, anything that would move this forward is good.
Image
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

I've got code that will read a DDS texture but unfortunately it's copyrighted and i can't give it out, but...

The good news is that it's a pretty easy format to read, even the compressed stuff, in opengl you just pass it to glCompressedTex2D (or similar) instead of glTexImage2D.

and i imagine in direct x it would be very simple too seeing as it's a direct x format.

I'm sure there are plenty of sites on the web showing you how it's done!
Image Image Image
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

The DDS specs should be in the DX SDK docu IIRC. It's basically a bunch of (compressed) images concatenated. And DDS files support a lot of pixel formats, that's why it's really nice to use it. Mipmap support is also nice, because you can store custom mipmaps that way, which wouldn't be possible if you regenerate them after loading.
CuteAlien
Admin
Posts: 9648
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Post by CuteAlien »

There is a working DDS implemenation for a very old irrlicht (0.14) here:
http://irrlicht.sourceforge.net/phpBB2/ ... hlight=dds

It only runs in OpenGL but that might be a good start if someone wants to implement it again. I tested that loader back then and it worked for me.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
dlangdev
Posts: 1324
Joined: Tue Aug 07, 2007 7:28 pm
Location: Beaverton OR
Contact:

Post by dlangdev »

Cool, this thread is helping me write the DDS texture loader for the B3D parser which I'm trying to finish.

Also found this webpage helpful.

http://www.codesampler.com/oglsrc.htm

Thanks for the help.
Image
night_hawk
Posts: 153
Joined: Mon Mar 03, 2008 8:42 am
Location: Suceava - Romania
Contact:

Post by night_hawk »

From what I know, although .DDS is larger in size, it's better than other formats because:
-It dumps it's content directly in the memory, without decompression or decoding or whatever, thus faster to load.
-Mip-maps.

I might be wrong, but that's pretty much what I've learned over my short experience with 3d programming.

EDIT: just clicked on a link above and it seems the person who wrote that early .DDS support said the exact same things as I did... dang it.
dlangdev
Posts: 1324
Joined: Tue Aug 07, 2007 7:28 pm
Location: Beaverton OR
Contact:

Post by dlangdev »

This was the gl function used in the sample program...

Code: Select all

            glCompressedTexImage2DARB( GL_TEXTURE_2D,
                                       i,
                                       pDDSImageData->format,
                                       nWidth,
                                       nHeight,
                                       0,
                                       nSize,
                                       pDDSImageData->pixels + nOffset );
Though what I need is to translate that to this one...

Code: Select all


gluBuild2DMipmaps(GL_TEXTURE_2D, 3, nWidth, nHeight, GL_RGB, pDDSImageData->format, pDDSImageData->pixels);

It turned that I need to walk the mipmap levels and use glCompressedTexImage2DARB() for each.
Image
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Yes, but you'd need that walk anyway, because older versions of GLU didn't support compression...
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

From Demon's CColorConverter class -

Code: Select all

//! copies DXT1 data to 32 bit A8R8G8B8 data
void CColorConverter::convertDXT1toA8R8G8B8(const u8* src, u32* dest, const s32& width, const s32& height )
{
	u32 srcIndex = 0;
	u32 destIndex = 0;
	u32 colors[4] = { 0,0,0,0 };

	// copy dxt1 to destination image
	for( s32 y=0; y<height; y += 4 )
	{
		for( s32 x=0; x<width; x += 4 )
		{
			// call get color lookups function
			getCompressedLookupColors( &src[ srcIndex ], colors );
			srcIndex += 4;

			// Now that we have our lookup colors, read in the 4x4 color index table
			u32 colorTable = 0;
			memcpy( &colorTable, &src[ srcIndex ], 4 );
			srcIndex += 4;

			u8 tmpIndex = 0;
			for( u8 xx=0; xx<4; xx++ )
			{
				for( u8 yy=0; yy<4; yy++ )
				{
					tmpIndex = ( colorTable >> ( ( xx + yy * 4 ) * 2 ) ) & 3;
					memcpy( &dest[ x + xx + ( y + yy ) * height ], &colors[ tmpIndex ], 4 );
					destIndex++;
				}
			}
		}
	}
}


//! copies DXT2/3 data to 32 bit A8R8G8B8 data
void CColorConverter::convertDXT3toA8R8G8B8(const u8* src, u32* dest, const s32& width, const s32& height )
{
	u32 srcIndex = 0;
	u32 destIndex = 0;
	u8 alphas[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
	u32 colors[4] = { 0,0,0,0 };

	for( s32 y=0; y<height; y += 4 )
	{
		for( s32 x=0; x<width; x += 4 )
		{
			// get alpha data first
			getDXT3Alphas( &src[ srcIndex ], alphas );
			srcIndex += 8;

			getCompressedLookupColors( &src[ srcIndex ], colors );
			srcIndex += 4;

			// Now that we have our lookup colors, read in the 4x4 color index table
			u32 colorTable = 0;
			memcpy( &colorTable, &src[ srcIndex ], 4 );
			srcIndex += 4;

			u8 tmpIndex = 0;
			u8 counter = 0;
			for( u8 xx=0; xx<4; xx++ )
			{
				for( u8 yy=0; yy<4; yy++ )
				{
					tmpIndex = ( colorTable >> ( ( xx + yy * 4 ) * 2 ) ) & 3;
					video::SColor tmpColor( colors[ tmpIndex ] );
					tmpColor.setAlpha( alphas[ counter++ ] );
					memcpy( &dest[ x + xx + ( y + yy ) * height ], &tmpColor.color, 4 );
					destIndex++;
				}
			}
		}
	}
}


//! copies DXT4/5 data to 32 bit A8R8G8B8 data
void CColorConverter::convertDXT5toA8R8G8B8(const u8* src, u32* dest, const s32& width, const s32& height )
{
	u32 srcIndex = 0;
	u32 destIndex = 0;
	u8 alphas[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
	u32 colors[4] = { 0,0,0,0 };

	for( s32 y=0; y<height; y += 4 )
	{
		for( s32 x=0; x<width; x += 4 )
		{
			// get alpha data first
			getDXT3Alphas( &src[ srcIndex ], alphas );
			srcIndex += 8;

			getCompressedLookupColors( &src[ srcIndex ], colors );
			srcIndex += 4;

			// Now that we have our lookup colors, read in the 4x4 color index table
			u32 colorTable = 0;
			memcpy( &colorTable, &src[ srcIndex ], 4 );
			srcIndex += 4;

			u8 tmpIndex = 0;
			u8 counter = 0;
			for( u8 xx=0; xx<4; xx++ )
			{
				for( u8 yy=0; yy<4; yy++ )
				{
					tmpIndex = ( colorTable >> ( ( xx + yy * 4 ) * 2 ) ) & 3;
					video::SColor tmpColor( colors[ tmpIndex ] );
					tmpColor.setAlpha( alphas[ counter++ ] );
					memcpy( &dest[ x + xx + ( y + yy ) * height ], &tmpColor.color, 4 );
					destIndex++;
				}
			}
		}
	}
}


//! gets the lookup colors for compressed images( DXT1 - 5 ) color table data
void CColorConverter::getCompressedLookupColors(const u8* data, u32* colors )
{
	u16 c0r5g6b5;
	memcpy( &c0r5g6b5, data, 2 );
	u16 c1r5g6b5;
	memcpy( &c1r5g6b5, data+2, 2 );

	colors[0] = R5G6B5toA8R8G8B8( c0r5g6b5 );
	video::SColor c0( colors[0] );
	colors[1] = R5G6B5toA8R8G8B8( c1r5g6b5 );
	video::SColor c1( colors[1] );

	video::SColor tmpColor( 255, 0, 0, 0 );
	if( c0r5g6b5 > c1r5g6b5 )
	{
		tmpColor.setRed( (u32)( ( c0.getRed() + c0.getRed() + c1.getRed() ) / 3 ) );
		tmpColor.setGreen( (u32)( ( c0.getGreen() + c0.getGreen() + c1.getGreen() ) / 3 ) );
		tmpColor.setBlue( (u32)( ( c0.getBlue() + c0.getBlue() + c1.getBlue() ) / 3 ) );
		colors[2] = tmpColor.toA8R8G8B8();

		tmpColor.setRed( (u32)( ( c0.getRed() + c1.getRed() + c1.getRed() ) / 3 ) );
		tmpColor.setGreen( (u32)( ( c0.getGreen() + c1.getGreen() + c1.getGreen() ) / 3 ) );
		tmpColor.setBlue( (u32)( ( c0.getBlue() + c1.getBlue() + c1.getBlue() ) / 3 ) );
		colors[3] = tmpColor.toA8R8G8B8();
	}
	else
	{
		tmpColor.set( 255,
			(u32)( c0.getRed() * 0.5f + c1.getRed() * 0.5f ),
			(u32)( c0.getGreen() * 0.5f + c1.getGreen() * 0.5f ),
			(u32)( c0.getBlue() * 0.5f + c1.getBlue() * 0.5f ) );
		colors[2] = tmpColor.toA8R8G8B8();

		colors[3] = 0;
	}
}


//! computes the alpha values for DXT2/3 formats
void CColorConverter::getDXT3Alphas(const u8* data, u8* alphas )
{
	u32 alpha[2];
	memcpy( &alpha[0], data, 4 );
	memcpy( &alpha[1], data+4, 4 );

	alphas[ 0] = (u8)( ( ( alpha[0] >> 28 ) & 0xf ) * 0xff / 0xf );
	alphas[ 1] = (u8)( ( ( alpha[0] >> 24 ) & 0xf ) * 0xff / 0xf );
	alphas[ 2] = (u8)( ( ( alpha[0] >> 20 ) & 0xf ) * 0xff / 0xf );
	alphas[ 3] = (u8)( ( ( alpha[0] >> 16 ) & 0xf ) * 0xff / 0xf );
	alphas[ 4] = (u8)( ( ( alpha[0] >> 12 ) & 0xf ) * 0xff / 0xf );
	alphas[ 5] = (u8)( ( ( alpha[0] >> 8 ) & 0xf ) * 0xff / 0xf );
	alphas[ 6] = (u8)( ( ( alpha[0] >> 4 ) & 0xf ) * 0xff / 0xf );
	alphas[ 7] = (u8)( ( alpha[0] & 0xf ) * 0xff / 0xf );
	alphas[ 8] = (u8)( ( ( alpha[1] >> 28 ) & 0xf ) * 0xff / 0xf );
	alphas[ 9] = (u8)( ( ( alpha[1] >> 24 ) & 0xf ) * 0xff / 0xf );
	alphas[10] = (u8)( ( ( alpha[1] >> 20 ) & 0xf ) * 0xff / 0xf );
	alphas[11] = (u8)( ( ( alpha[1] >> 16 ) & 0xf ) * 0xff / 0xf );
	alphas[12] = (u8)( ( ( alpha[1] >> 12 ) & 0xf ) * 0xff / 0xf );
	alphas[13] = (u8)( ( ( alpha[1] >> 8 ) & 0xf ) * 0xff / 0xf );
	alphas[14] = (u8)( ( ( alpha[1] >> 4 ) & 0xf ) * 0xff / 0xf );
	alphas[15] = (u8)( ( alpha[1] & 0xf ) * 0xff / 0xf );
}


//! computes the alpha values for DXT4/5 formats
void CColorConverter::getDXT5Alphas(const u8* data, u8* alphas )
{
	// alpha data comes first
	u8 alphaLookup[8];
	memcpy( &alphaLookup[0], data, 1 );
	memcpy( &alphaLookup[1], data+1, 1 );

	if( alphaLookup[0] > alphaLookup[1] )
	{
		alphaLookup[2] = ( 6 * alphaLookup[0] + 1 * alphaLookup[1] + 3 ) / 7;
		alphaLookup[3] = ( 5 * alphaLookup[0] + 2 * alphaLookup[1] + 3 ) / 7;
		alphaLookup[4] = ( 4 * alphaLookup[0] + 3 * alphaLookup[1] + 3 ) / 7;
		alphaLookup[5] = ( 3 * alphaLookup[0] + 4 * alphaLookup[1] + 3 ) / 7;
		alphaLookup[6] = ( 2 * alphaLookup[0] + 5 * alphaLookup[1] + 3 ) / 7;
		alphaLookup[7] = ( 1 * alphaLookup[0] + 6 * alphaLookup[1] + 3 ) / 7;
	}
	else
	{
		alphaLookup[2] = ( 4 * alphaLookup[0] + 1 * alphaLookup[1] + 2 ) / 5;
		alphaLookup[3] = ( 3 * alphaLookup[0] + 2 * alphaLookup[1] + 2 ) / 5;
		alphaLookup[4] = ( 2 * alphaLookup[0] + 3 * alphaLookup[1] + 2 ) / 5;
		alphaLookup[5] = ( 1 * alphaLookup[0] + 4 * alphaLookup[1] + 2 ) / 5;
		alphaLookup[6] = 0;
		alphaLookup[7] = 255;
	}

	// next 48 bits are the 4x4 3bit values representing which alpha value from the lookup table is for which color
	u8 alphaRef[6];
	memcpy( alphaRef, data+2, 6 );
	
	alphas[ 0] = (u8)( alphaLookup[ alphaRef[0] & 7 ] );
	alphas[ 1] = (u8)( alphaLookup[ ( alphaRef[0] >> 3 ) & 7 ] );
	alphas[ 2] = (u8)( alphaLookup[ ( ( alphaRef[1] & 1 ) | ( alphaRef[0] >> 6 ) ) & 3 ] );
	alphas[ 3] = (u8)( alphaLookup[ ( alphaRef[1] >> 1 ) & 7 ] );
	alphas[ 4] = (u8)( alphaLookup[ ( alphaRef[1] >> 4 ) & 7 ] );
	alphas[ 5] = (u8)( alphaLookup[ ( ( alphaRef[2] & 3 ) | ( alphaRef[1] >> 7 ) ) ] );
	alphas[ 6] = (u8)( alphaLookup[ ( alphaRef[2] >> 2 ) & 7 ] );
	alphas[ 7] = (u8)( alphaLookup[ ( alphaRef[2] >> 5 ) & 7 ] );
	alphas[ 8] = (u8)( alphaLookup[ alphaRef[3] & 7 ] );
	alphas[ 9] = (u8)( alphaLookup[ ( alphaRef[3] >> 3 ) & 7 ] );
	alphas[10] = (u8)( alphaLookup[ ( ( alphaRef[4] & 1 ) | ( alphaRef[3] >> 6 ) ) & 3 ] );
	alphas[11] = (u8)( alphaLookup[ ( alphaRef[4] >> 1 ) & 7 ] );
	alphas[12] = (u8)( alphaLookup[ ( alphaRef[4] >> 4 ) & 7 ] );
	alphas[13] = (u8)( alphaLookup[ ( ( alphaRef[5] & 3 ) | ( alphaRef[4] >> 7 ) ) ] );
	alphas[14] = (u8)( alphaLookup[ ( alphaRef[5] >> 2 ) & 7 ] );
	alphas[15] = (u8)( alphaLookup[ ( alphaRef[5] >> 5 ) & 7 ] );
}
And the CImageLoaderDDS.h -

Code: Select all

// Copyright (C) 2002-2007 Thomas Ince
// This file is part of the "3Demon Engine".
// For conditions of distribution and use, see copyright notice in 3Demon.h

#ifndef __C_IMAGE_LOADER_DDS_H_INCLUDED__
#define __C_IMAGE_LOADER_DDS_H_INCLUDED__

#include "CompileConfig.h"
#include "IImageLoader.h"
#include <stdio.h>

namespace demon
{
namespace video
{
	class IVideoDriver;

	class CImageLoaderDDS : public IImageLoader
	{
	public:
		//! constructor
		CImageLoaderDDS( IVideoDriver* driver );

		//! destructor
		virtual ~CImageLoaderDDS();

		//! returns true if the file maybe is able to be loaded by this class
		//! based on the file extension (e.g. ".tga")
		virtual bool isALoadableFileExtension(const c8* fileName);

		//! returns true if the file maybe is able to be loaded by this class
		virtual bool isALoadableFileFormat(demon::io::IReadFile* file);

		//! creates a surface from the file
		virtual IImage* loadImage(demon::io::IReadFile* file);

	private:
		IVideoDriver* Driver;

		struct S_PIXEL_FORMAT
		{
			u32	size;
			u32 flags;
			u32 fourCC;
			u32 RGBBitCount;
			u32	redBitMask;
			u32 greenBitMask;
			u32 blueBitMask;
			u32	RGBAlphaMask;
		};

		struct S_CAPS2
		{
			u32	caps1;
			u32	caps2;
			u32	reserved[2];
		};

		struct S_SURFACE_FORMAT_HEADER
		{
			u32				size;
			u32				flags;
			u32				height;
			u32				width;
			u32				pitch;
			u32				depth;
			u32				mipMapCount;
			u32				reserved1[11];
			S_PIXEL_FORMAT	pixelFormat;
			S_CAPS2			caps;
			u32				reserved2;
		};

		struct S_R5G6B5
		{
			u16 b : 5;
			u16 g : 6;
			u16 r : 5;

			u32 toA8R8G8B8()
			{
				video::SColor tmp( 255,
					r * 0xFF / 0x1F,
					g * 0xFF / 0x3F,
					b * 0xFF / 0x1F );
				return tmp.toA8R8G8B8();
				//return 0xFF000000 | ( ( r * 0xFF ) / 0x1F ) | ( ( g * 0xFF ) / 0x3F ) | ( ( b * 0xFF ) / 0x1F );
			}

			bool operator > ( const S_R5G6B5& other ) const
			{
				video::SColor tmp1( 255,
					r * 0xFF / 0x1F,
					g * 0xFF / 0x3F,
					b * 0xFF / 0x1F );
				video::SColor tmp2( 255,
					other.r * 0xFF / 0x1F,
					other.g * 0xFF / 0x3F,
					other.b * 0xFF / 0x1F );
				return tmp1.toA8R8G8B8() > tmp2.toA8R8G8B8();
				//return (*(u16*)this) > (*(u16*)&other);
			}
		};
	};

} // end namespace video
} // end namespace demon


#endif // __C_IMAGE_LOADER_DDS_H_INCLUDED__
And finally, the CImageLoaderDDS.cpp file -

Code: Select all

// Copyright (C) 2002-2007 Thomas Ince
// This file is part of the "3Demon Engine".
// For conditions of distribution and use, see copyright notice in 3Demon.h

#include "CompileConfig.h"
#include "CImageLoaderDDS.h"
#include "IVideoDriver.h"
#include "DemonString.h"
#include "CImage.h"
#include "os.h"

#define DDS_MAGIC			0x20534444

//  S_SURFACE_FORMAT_HEADER.flags
#define DDSD_CAPS			0x00000001
#define DDSD_HEIGHT			0x00000002
#define DDSD_WIDTH			0x00000004
#define DDSD_PITCH			0x00000008
#define DDSD_PIXELFORMAT	0x00001000
#define DDSD_MIPMAPCOUNT	0x00020000
#define DDSD_LINEARSIZE		0x00080000
#define DDSD_DEPTH			0x00800000

//  S_SURFACE_FORMAT_HEADER.S_PIXEL_FORMAT.flags
#define DDPF_ALPHAPIXELS	0x00000001
#define DDPF_ALPHA			0x00000002
#define DDPF_FOURCC			0x00000004
#define DDPF_RGB			0x00000040
#define DDPF_COMPRESSED		0x00000080
#define DDPF_LUMINANCE		0x00020000

//  S_SURFACE_FORMAT_HEADER.caps.caps1
#define DDSCAPS1_COMPLEX	0x00000008
#define DDSCAPS1_TEXTURE	0x00001000
#define DDSCAPS1_MIPMAP		0x00400000

//  S_SURFACE_FORMAT_HEADER.caps.caps2
#define DDSCAPS2_CUBEMAP            0x00000200
#define DDSCAPS2_CUBEMAP_POSITIVEX  0x00000400
#define DDSCAPS2_CUBEMAP_NEGATIVEX  0x00000800
#define DDSCAPS2_CUBEMAP_POSITIVEY  0x00001000
#define DDSCAPS2_CUBEMAP_NEGATIVEY  0x00002000
#define DDSCAPS2_CUBEMAP_POSITIVEZ  0x00004000
#define DDSCAPS2_CUBEMAP_NEGATIVEZ  0x00008000
#define DDSCAPS2_VOLUME             0x00200000

#ifndef MAKEFOURCC
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
	((s32)(u8)(ch0) | ((s32)(u8)(ch1) << 8) | \
	((s32)(u8)(ch2) << 16) | ((s32)(u8)(ch3) << 24 ))
#endif

#define D3DFMT_R8G8B8               20
#define D3DFMT_A8R8G8B8             21
#define D3DFMT_X8R8G8B8             22
#define D3DFMT_R5G6B5               23
#define D3DFMT_A1R5G5B5             25
#define D3DFMT_A4R4G4B4             26
#define D3DFMT_A8B8G8R8             32
#define D3DFMT_X8B8G8R8             33
#define D3DFMT_DXT1                 MAKEFOURCC('D', 'X', 'T', '1')
#define D3DFMT_DXT2                 MAKEFOURCC('D', 'X', 'T', '2')
#define D3DFMT_DXT3                 MAKEFOURCC('D', 'X', 'T', '3')
#define D3DFMT_DXT4                 MAKEFOURCC('D', 'X', 'T', '4')
#define D3DFMT_DXT5                 MAKEFOURCC('D', 'X', 'T', '5')

namespace demon
{
namespace video
{
	//! constructor
	CImageLoaderDDS::CImageLoaderDDS( IVideoDriver* driver )
	: Driver( driver )
	{
#ifdef _DEBUG
		setDebugName("CImageLoaderDDS");
#endif
	}

	//! destructor
	CImageLoaderDDS::~CImageLoaderDDS()
	{
	}

	//! returns true if the file maybe is able to be loaded by this class
	//! based on the file extension (e.g. ".tga")
	bool CImageLoaderDDS::isALoadableFileExtension(const c8* fileName)
	{
		return strstr(fileName, ".dds") != 0;
	}

	//! returns true if the file maybe is able to be loaded by this class
	bool CImageLoaderDDS::isALoadableFileFormat(demon::io::IReadFile* file)
	{
		if(!file)
			return false;

		char magicWord[4];
		file->read( &magicWord, 4 );
		return (magicWord[0] == 'D' && magicWord[1] == 'D' && magicWord[2] == 'S' );
	}

	//! creates a surface from the file
	IImage* CImageLoaderDDS::loadImage(demon::io::IReadFile* file)
	{
		IImage* image = NULL;
		S_SURFACE_FORMAT_HEADER	header;

		file->seek( 4 );
		file->read( &header, sizeof( S_SURFACE_FORMAT_HEADER ) );

		if( header.size == 124 && ( header.flags & DDSD_PIXELFORMAT ) && ( header.flags & DDSD_CAPS ) )
		{
			// determine if texture is 3d( has depth )
			bool is3D = header.depth > 0 && ( header.flags & DDSD_DEPTH );

			if( !is3D )
				header.depth = 1;

			// determine if texture has alpha
			bool usingAlpha = header.pixelFormat.flags & DDPF_ALPHAPIXELS;

			// color format to create image
			E_COLOR_FORMAT format = ECF_UNKNOWN;

			u32 dataSize = 0;

			if( header.pixelFormat.flags & DDPF_RGB )
			{
				u32 byteCount = header.pixelFormat.RGBBitCount / 8;
			
				if( header.flags & DDSD_PITCH )
					dataSize = header.pitch * header.height * header.depth * ( header.pixelFormat.RGBBitCount / 8 );
				else
					dataSize = header.width * header.height * header.depth * ( header.pixelFormat.RGBBitCount / 8 );

				u8* data = new u8[dataSize];
				file->read( data, dataSize );
				
				// has an alpha mask
				switch( header.pixelFormat.RGBBitCount )
				{
					case 16:
					{
						if( usingAlpha )
						{
							if( header.pixelFormat.RGBAlphaMask == 0x8000 )
							{
								format = ECF_A1R5G5B5;
								os::Printer::print( "DDS : ECF_A1R5G5B5 format" );
							}
							else if( header.pixelFormat.RGBAlphaMask == 0xf000 )
							{
								format = ECF_A4R4G4B4;
								os::Printer::print( "DDS : ECF_A4R4G4B4 format" );
							}
							else
							{
								os::Printer::print( "DDS : Unsupported 16 bit format with alpha" );
							}
						}
						else
						{
							if( header.pixelFormat.redBitMask == 0xf800 )
							{
								format = ECF_R5G6B5;
								os::Printer::print( "DDS : ECF_R5G6B5 format" );
							}
						}
						break;
					}
					case 24:
					{
						if( usingAlpha )
						{
							os::Printer::print( "DDS : Unsupported 24 bit format with alpha" );
						}
						else
						{
							if( header.pixelFormat.redBitMask & 0xff0000 )
							{
								format = ECF_R8G8B8;
								os::Printer::print( "DDS : ECF_R8G8B8 format" );
							}
						}
						break;
					}
					case 32:
					{
						if( usingAlpha )
						{
							if( header.pixelFormat.redBitMask & 0xff0000 )
							{
								format = ECF_A8R8G8B8;
								os::Printer::print( "DDS : ECF_A8R8G8B8 format" );
							}
							else if( header.pixelFormat.redBitMask & 0xff )
							{
								format = ECF_A8R8G8B8;
								os::Printer::print( "DDS : ECF_A8R8G8B8 format" );

								// convert data from A8B8G8R8 to A8R8G8B8
								u8 tmp = 0;
								for( u32 x=0; x<dataSize; x+=4 )
								{
									tmp = data[x];
									data[x] = data[x+2];
									data[x+2] = tmp;
								}
							}
							else
							{
								os::Printer::print( "DDS : Unsupported 32 bit alpha format" );
							}
						}
						else
						{
							if( header.pixelFormat.redBitMask & 0xff0000 )
							{
								format = ECF_X8R8G8B8;
								os::Printer::print( "DDS : ECF_X8R8G8B8 format" );
							}
							else if( header.pixelFormat.redBitMask & 0xff )
							{
								format = ECF_X8R8G8B8;
								os::Printer::print( "DDS : ECF_X8R8G8B8 format" );

								// convert data from X8B8G8R8 to X8R8G8B8
								u8 tmp = 0;
								for( u32 x=0; x<dataSize; x+=4 )
								{
									data[x+3] = 255;
									tmp = data[x];
									data[x] = data[x+2];
									data[x+2] = tmp;
								}
							}
						}
						break;
					}
				}

				if( format != ECF_UNKNOWN )
				{
					if( is3D )
						image = new CImage( format, core::vector3d< s32 >( header.width, header.height, header.depth ), data );
					else
						image = new CImage( format, core::vector2di( header.width, header.height ), data );
				}
			}
			else if( header.pixelFormat.flags & DDPF_FOURCC )
			{
				switch( header.pixelFormat.fourCC )
				{
					case D3DFMT_DXT1:
					{
						dataSize = ( header.width / 4 ) * ( header.height / 4 ) * 8;
						format = ECF_DXT1;
						os::Printer::print( "DDS : ECF_DXT1 format" );
						break;
					}
					case D3DFMT_DXT2:
					case D3DFMT_DXT3:
					{
						dataSize = ( header.width / 4 ) * ( header.height / 4 ) * 16;
						format = ECF_DXT3;
						os::Printer::print( "DDS : ECF_DXT3 format" );
						break;
					}
					case D3DFMT_DXT4:
					case D3DFMT_DXT5:
					{
						dataSize = ( header.width / 4 ) * ( header.height / 4 ) * 16;
						format = ECF_DXT5;
						os::Printer::print( "DDS : ECF_DXT5 format" );
						break;
					}
				}

				if( format != ECF_UNKNOWN )
				{
					u8* data = new u8[ dataSize ];
					file->read( data, dataSize );

					if( is3D )
						image = new CImage( format, core::vector3d< s32 >( header.width, header.height, header.depth ), data );
					else
						image = new CImage( format, core::vector2di( header.width, header.height ), data );
				}
			}
			else if( header.pixelFormat.flags & DDPF_LUMINANCE ) // 8-bit luminance [ D3DFMT_L8 ]
			{
				format = ECF_L8;
				dataSize = header.width * header.height * header.depth;

				if( format != ECF_UNKNOWN )
				{
					u8* data = new u8[ dataSize ];
					file->read( data, dataSize );
	
					if( is3D )
						image = new CImage( format, core::vector3d< s32 >( header.width, header.height, header.depth ), data );
					else
						image = new CImage( format, core::vector2di( header.width, header.height ), data );
				}
			}
			else
			{
				os::Printer::print( "UNKNOWN DDS FORMAT TEXTURE" );
			}
		}

		return image;
	}

	//! creates a loader which is able to load DDS images
	IImageLoader* createImageLoaderDDS( video::IVideoDriver* driver )
	{
		return new CImageLoaderDDS( driver );
	}

} // end namespace video
} // end namespace demon
If Irrlicht added support for the additional texture formats, then the ImageLoader should be mostly copy/paste to get working with Irrlicht. If not, the CColorConverter functions, will allow you to create the Irrlicht supported image data from the DDS compressed texture formats.

Supporting MipMaps with DDS isn't easy, because the IImage/CImage classes need to have support for MipMaps as well then.
Image
vvv
Posts: 11
Joined: Mon Feb 25, 2008 4:56 pm

Post by vvv »

2 Spintz

Why do you use const s32 &?
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

Because it's faster, and proper coding practice, IMO.

EDIT: I guess, since I have a good idea about the discussion that is to follow with this loaded question (it's happened before on these forums), I'll validate my response one more time.

Under typical conditions, the difference with a 32-bit arguments being passed by copy, or passed by reference is very minimal, if any difference at all. However, consider 64 heavy recursion, and check out the following test program -

The VxTimer.h file came from drac_gd, he posted it on the old IrrSpintz forums at - http://irrlicht.spintz.com/smforums/ind ... 45#msg1845

Code: Select all

#include <stdio.h>
#include "VxTimer.h"

int counter = 0;
const int callCount1 = 4000;
const int callCount2 = 4000;

void func1( int i );
void func2( int i );
void func3( const int& i );
void func4( const int& i );

void func1( int i )
{
	counter++;

	if( counter < callCount1 )
		func2( i );
}

void func2( int i )
{
	counter++;

	if( counter < callCount1 )
		func1( i );
}

void func3( const int& i )
{
	counter++;

	if( counter < callCount2 )
		func4( i );
}

void func4( const int& i )
{
	counter++;

	if( counter < callCount2 )
		func3( i );
}

int main()
{
	VxTimer timer1;
	timer1.StartMilliTimer( 0.0 );
	counter = 0;
	func1( counter );
	printf( "copy arg - %6.6f milliseconds\n", timer1.ElapsedMilliSec() );

	VxTimer timer2;
	timer2.StartMilliTimer( 0.0 );
	counter = 0;
	func3( counter );
	printf( "const ref arg - %6.6f milliseconds\n", timer2.ElapsedMilliSec() );

	return 0;
}
In Debug mode, on my machine, the output is -

Code: Select all

copy arg - 0.774121 milliseconds
const ref arg - 0.186686 milliseconds
In release mode, the Visual Studio compiler optimizes well, so the difference is much smaller -

Code: Select all

copy arg - 0.002375 milliseconds
const ref arg - 0.002514 milliseconds
However, there is a difference. And especially in Debug mode, it's much faster. Granted these are rare occurences, but rather than trying to optimize code for when these specific instances may occur, I just do my best to always use const& where possible.

These results prove to me, that in some cases, there can be a speed improvement with const&, and so I use it, as it doesn't slow it down to use it in cases where it's really not needed.

EDIT2: Also, let me apologize before hand, in case this response offends you or anyone else. I've discussed this topic many times before, on these forums and on other forums and personal discussions and it usually leads to a very heated discussion.
Image
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

However, these results are heavily platform dependent and probably also due to the very simplistic function bodies. For example, 64bit machines have to push the double amount of bytes onto the stack, and dereferencing the pointers after a cache flush would likely lead to much worse numbers for the const& case.
Moreover, you can also get slightly better results for the first case when using 'const s32', because the parameter is not changed. And in release mode you have not only a smaller difference, but an inversion - the const& is slower!
But I guess this is far too OT from the original post...
Post Reply