DDS Support
DDS Support
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.
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...
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...
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!
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!
-
- Admin
- Posts: 14143
- Joined: Wed Apr 19, 2006 9:20 pm
- Location: Oldenburg(Oldb), Germany
- Contact:
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.
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.
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
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
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.
Also found this webpage helpful.
http://www.codesampler.com/oglsrc.htm
Thanks for the help.
-
- Posts: 153
- Joined: Mon Mar 03, 2008 8:42 am
- Location: Suceava - Romania
- Contact:
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.
-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.
This was the gl function used in the sample program...
Though what I need is to translate that to this one...
It turned that I need to walk the mipmap levels and use glCompressedTexImage2DARB() for each.
Code: Select all
glCompressedTexImage2DARB( GL_TEXTURE_2D,
i,
pDDSImageData->format,
nWidth,
nHeight,
0,
nSize,
pDDSImageData->pixels + nOffset );
Code: Select all
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, nWidth, nHeight, GL_RGB, pDDSImageData->format, pDDSImageData->pixels);
From Demon's CColorConverter class -
And the CImageLoaderDDS.h -
And finally, the CImageLoaderDDS.cpp file -
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.
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 ] );
}
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__
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
Supporting MipMaps with DDS isn't easy, because the IImage/CImage classes need to have support for MipMaps as well then.
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
In Debug mode, on my machine, the output is -
In release mode, the Visual Studio compiler optimizes well, so the difference is much smaller -
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.
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;
}
Code: Select all
copy arg - 0.774121 milliseconds
const ref arg - 0.186686 milliseconds
Code: Select all
copy arg - 0.002375 milliseconds
const ref arg - 0.002514 milliseconds
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.
-
- Admin
- Posts: 14143
- Joined: Wed Apr 19, 2006 9:20 pm
- Location: Oldenburg(Oldb), Germany
- Contact:
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...
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...