Browse Source

Expanded DDS support

- DX10 format support (only data formats compatible)
- Images may be "chained" as a list off of the first image (DDS
cubemaps, texture arrays)
- Direct3D9 cubemap loading from DDS cubemap
JSandusky 10 years ago
parent
commit
f0a7f30fa1

+ 69 - 58
Source/Urho3D/Graphics/Direct3D9/D3D9TextureCube.cpp

@@ -120,73 +120,84 @@ bool TextureCube::BeginLoad(Deserializer& source)
         // If path is empty, add the XML file path
         if (GetPath(name).Empty())
             name = texPath + name;
-
-        CubeMapLayout layout =
-            (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
+        
         SharedPtr<Image> image = cache->GetTempResource<Image>(name);
         if (!image)
             return false;
-
+        
         int faceWidth, faceHeight;
         loadImages_.Resize(MAX_CUBEMAP_FACES);
 
-        switch (layout)
+        if (image->IsCubemap())
         {
-        case CML_HORIZONTAL:
-            faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
-            faceHeight = image->GetHeight();
-            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
-            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 3, 0, faceWidth, faceHeight);
-            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 4, 0, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 5, 0, faceWidth, faceHeight);
-            break;
-
-        case CML_HORIZONTALNVIDIA:
-            faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
-            faceHeight = image->GetHeight();
-            for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
-                loadImages_[i] = GetTileImage(image, i, 0, faceWidth, faceHeight);
-            break;
-
-        case CML_HORIZONTALCROSS:
-            faceWidth = image->GetWidth() / 4;
-            faceHeight = image->GetHeight() / 3;
-            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
-            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
-            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 3, 1, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
-            break;
-
-        case CML_VERTICALCROSS:
-            faceWidth = image->GetWidth() / 3;
-            faceHeight = image->GetHeight() / 4;
-            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
-            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
-            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 3, faceWidth, faceHeight);
-            if (loadImages_[FACE_NEGATIVE_Z])
+            loadImages_[FACE_POSITIVE_X] = image;
+            loadImages_[FACE_NEGATIVE_X] = loadImages_[FACE_POSITIVE_X]->GetNextSibling();
+            loadImages_[FACE_POSITIVE_Y] = loadImages_[FACE_NEGATIVE_X]->GetNextSibling();
+            loadImages_[FACE_NEGATIVE_Y] = loadImages_[FACE_POSITIVE_Y]->GetNextSibling();
+            loadImages_[FACE_POSITIVE_Z] = loadImages_[FACE_NEGATIVE_Y]->GetNextSibling();
+            loadImages_[FACE_NEGATIVE_Z] = loadImages_[FACE_POSITIVE_Z]->GetNextSibling();
+        } 
+        else
+        {
+            CubeMapLayout layout = (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
+            switch (layout)
             {
-                loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
-                loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal();
+            case CML_HORIZONTAL:
+                faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
+                faceHeight = image->GetHeight();
+                loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
+                loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 3, 0, faceWidth, faceHeight);
+                loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 4, 0, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 5, 0, faceWidth, faceHeight);
+                break;
+
+            case CML_HORIZONTALNVIDIA:
+                faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
+                faceHeight = image->GetHeight();
+                for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+                    loadImages_[i] = GetTileImage(image, i, 0, faceWidth, faceHeight);
+                break;
+
+            case CML_HORIZONTALCROSS:
+                faceWidth = image->GetWidth() / 4;
+                faceHeight = image->GetHeight() / 3;
+                loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
+                loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
+                loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 3, 1, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
+                break;
+
+            case CML_VERTICALCROSS:
+                faceWidth = image->GetWidth() / 3;
+                faceHeight = image->GetHeight() / 4;
+                loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
+                loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
+                loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 3, faceWidth, faceHeight);
+                if (loadImages_[FACE_NEGATIVE_Z])
+                {
+                    loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
+                    loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal();
+                }
+                break;
+
+            case CML_BLENDER:
+                faceWidth = image->GetWidth() / 3;
+                faceHeight = image->GetHeight() / 2;
+                loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+                loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
+                loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
+                loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
+                loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
+                break;
             }
-            break;
-
-        case CML_BLENDER:
-            faceWidth = image->GetWidth() / 3;
-            faceHeight = image->GetHeight() / 2;
-            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
-            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
-            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
-            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
-            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
-            break;
         }
     }
     // Face per image

+ 216 - 60
Source/Urho3D/Resource/Image.cpp

@@ -47,6 +47,37 @@ extern "C" unsigned char* stbi_write_png_to_mem(unsigned char* pixels, int strid
 #define FOURCC_DXT3 (MAKEFOURCC('D','X','T','3'))
 #define FOURCC_DXT4 (MAKEFOURCC('D','X','T','4'))
 #define FOURCC_DXT5 (MAKEFOURCC('D','X','T','5'))
+#define FOURCC_DX10 (MAKEFOURCC('D','X','1','0'))
+
+static const unsigned DDSCAPS_COMPLEX = 0x00000008U;
+static const unsigned DDSCAPS_TEXTURE = 0x00001000U;
+static const unsigned DDSCAPS_MIPMAP = 0x00400000U;
+static const unsigned DDSCAPS2_VOLUME = 0x00200000U;
+static const unsigned DDSCAPS2_CUBEMAP = 0x00000200U;
+
+static const unsigned DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400U;
+static const unsigned DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800U;
+static const unsigned DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000U;
+static const unsigned DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000U;
+static const unsigned DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000U;
+static const unsigned DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000U;
+static const unsigned DDSCAPS2_CUBEMAP_ALL_FACES = 0x0000FC00U;
+
+// DX10 flags
+static const unsigned DDS_DIMENSION_TEXTURE1D = 2;
+static const unsigned DDS_DIMENSION_TEXTURE2D = 3;
+static const unsigned DDS_DIMENSION_TEXTURE3D = 4;
+
+static const unsigned DDS_RESOURCE_MISC_TEXTURECUBE = 0x4;
+
+static const unsigned DDS_DXGI_FORMAT_R8G8B8A8_UNORM = 28;
+static const unsigned DDS_DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 26;
+static const unsigned DDS_DXGI_FORMAT_BC1_UNORM = 71;
+static const unsigned DDS_DXGI_FORMAT_BC1_UNORM_SRGB = 72;
+static const unsigned DDS_DXGI_FORMAT_BC2_UNORM = 74;
+static const unsigned DDS_DXGI_FORMAT_BC2_UNORM_SRGB = 75;
+static const unsigned DDS_DXGI_FORMAT_BC3_UNORM = 77;
+static const unsigned DDS_DXGI_FORMAT_BC3_UNORM_SRGB = 78;
 
 namespace Urho3D
 {
@@ -125,6 +156,15 @@ struct DDSCaps2
     };
 };
 
+struct DDSHeader10
+{
+    unsigned dxgiFormat;
+    unsigned resourceDimension;
+    unsigned miscFlag;
+    unsigned arraySize;
+    unsigned reserved;
+};
+
 /// DirectDraw surface description.
 struct DDSurfaceDesc2
 {
@@ -203,7 +243,10 @@ Image::Image(Context* context) :
     width_(0),
     height_(0),
     depth_(0),
-    components_(0)
+    components_(0),
+    cubemap_(false),
+    array_(false),
+    sRGB_(false)
 {
 }
 
@@ -226,8 +269,51 @@ bool Image::BeginLoad(Deserializer& source)
         // DDS compressed format
         DDSurfaceDesc2 ddsd;
         source.Read(&ddsd, sizeof(ddsd));
+        
+        // DDS DX10+
+        const bool hasDXGI = ddsd.ddpfPixelFormat_.dwFourCC_ == FOURCC_DX10;
+        DDSHeader10 dxgiHeader;
+        if (hasDXGI)
+            source.Read(&dxgiHeader, sizeof(dxgiHeader));
+
+        unsigned fourCC = ddsd.ddpfPixelFormat_.dwFourCC_;
+        
+        // If the DXGI header is available then remap formats and check sRGB
+        if (hasDXGI)
+        {
+            switch (dxgiHeader.dxgiFormat)
+            {
+            case DDS_DXGI_FORMAT_BC1_UNORM:
+            case DDS_DXGI_FORMAT_BC1_UNORM_SRGB:
+                fourCC = FOURCC_DXT1;
+                break;
+            case DDS_DXGI_FORMAT_BC2_UNORM:
+            case DDS_DXGI_FORMAT_BC2_UNORM_SRGB:
+                fourCC = FOURCC_DXT3;
+                break;
+            case DDS_DXGI_FORMAT_BC3_UNORM:
+            case DDS_DXGI_FORMAT_BC3_UNORM_SRGB:
+                fourCC = FOURCC_DXT5;
+                break;
+            case DDS_DXGI_FORMAT_R8G8B8A8_UNORM:
+            case DDS_DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+                fourCC = 0;
+                break;
+            default:
+                LOGERROR("Unrecognized DDS DXGI image format");
+                return false;
+            }
 
-        switch (ddsd.ddpfPixelFormat_.dwFourCC_)
+            // Check the internal sRGB formats
+            if (dxgiHeader.dxgiFormat == DDS_DXGI_FORMAT_BC1_UNORM_SRGB ||
+                dxgiHeader.dxgiFormat == DDS_DXGI_FORMAT_BC2_UNORM_SRGB ||
+                dxgiHeader.dxgiFormat == DDS_DXGI_FORMAT_BC3_UNORM_SRGB ||
+                dxgiHeader.dxgiFormat == DDS_DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
+            {
+                sRGB_ = true;
+            }
+        }
+        switch (fourCC)
         {
         case FOURCC_DXT1:
             compressedFormat_ = CF_DXT1;
@@ -260,105 +346,175 @@ bool Image::BeginLoad(Deserializer& source)
             return false;
         }
 
-        unsigned dataSize = source.GetSize() - source.GetPosition();
-        data_ = new unsigned char[dataSize];
-        width_ = ddsd.dwWidth_;
-        height_ = ddsd.dwHeight_;
-        depth_ = ddsd.dwDepth_;
-        numCompressedLevels_ = ddsd.dwMipMapCount_;
-        if (!numCompressedLevels_)
-            numCompressedLevels_ = 1;
-        SetMemoryUse(dataSize);
-        source.Read(data_.Get(), dataSize);
+        // Is it a cube map or texture array? If so determine the size of the image chain.
+        cubemap_ = (ddsd.ddsCaps_.dwCaps2_ & DDSCAPS2_CUBEMAP_ALL_FACES) != 0 || (hasDXGI && (dxgiHeader.miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE) != 0);
+        unsigned imageChainCount = 1;
+        if (cubemap_)
+            imageChainCount = 6;
+        else if (hasDXGI && dxgiHeader.arraySize > 1)
+        {
+            imageChainCount = dxgiHeader.arraySize;
+            array_ = true;
+        }
 
+        // Calculate the size of the data
+        unsigned dataSize = 0;
+        if (compressedFormat_ != CF_RGBA)
+        {
+            const unsigned blockSize = compressedFormat_ == CF_DXT1 ? 8 : 16; //DXT1/BC1 is 8 bytes, DXT3/BC2 and DXT5/BC3 are 16 bytes
+            // Add 3 to ensure valid block: ie 2x2 fits uses a whole 4x4 block
+            unsigned blocksWide = (ddsd.dwWidth_ + 3) / 4;
+            unsigned blocksHeight = (ddsd.dwHeight_ + 3) / 4;
+            dataSize = blocksWide * blocksHeight * blockSize;
+            
+            // Calculate mip data size
+            unsigned x = ddsd.dwWidth_ / 2;
+            unsigned y = ddsd.dwHeight_ / 2;
+            unsigned z = ddsd.dwDepth_ / 2;
+            for (unsigned level = ddsd.dwMipMapCount_; level > 0; x /= 2, y /= 2, z /= 2, level -= 1)
+            {
+                blocksWide = (x + 3) / 4;
+                blocksHeight = (y + 3) / 4;
+                dataSize += blockSize * blocksWide * blocksWide * Max(z, 1);
+            }
+        }
+        else
+        {
+            dataSize = (ddsd.ddpfPixelFormat_.dwRGBBitCount_ / 8) * ddsd.dwWidth_ * ddsd.dwHeight_ * Max(ddsd.dwDepth_, 1);
+            // Calculate mip data size
+            unsigned x = ddsd.dwWidth_ / 2;
+            unsigned y = ddsd.dwHeight_ / 2;
+            unsigned z = ddsd.dwDepth_ / 2;
+            for (unsigned level = ddsd.dwMipMapCount_; level > 0; x /= 2, y /= 2, z /= 2, level -= 1)
+                dataSize += (ddsd.ddpfPixelFormat_.dwRGBBitCount_ / 8) * x * y * Max(z, 1);
+        }
+
+        SharedPtr<Image> currentImage(this);
+
+        for (unsigned faceIndex = 0; faceIndex < imageChainCount; ++faceIndex)
+        {                
+            currentImage->data_ = new unsigned char[dataSize];
+            currentImage->cubemap_ = cubemap_;
+            currentImage->array_ = array_;
+            currentImage->components_ = components_;
+            currentImage->compressedFormat_ = compressedFormat_;
+            currentImage->width_ = ddsd.dwWidth_;
+            currentImage->height_ = ddsd.dwHeight_;
+            currentImage->depth_ = ddsd.dwDepth_;
+
+            currentImage->numCompressedLevels_ = ddsd.dwMipMapCount_;
+            if (!currentImage->numCompressedLevels_)
+                currentImage->numCompressedLevels_ = 1;
+                
+            if (faceIndex == 0)
+                currentImage->SetMemoryUse(dataSize * imageChainCount);
+            else
+                currentImage->SetMemoryUse(dataSize);
+
+            source.Read(currentImage->data_.Get(), dataSize);
+
+            if (faceIndex < imageChainCount - 1)
+            {
+                // Build the image chain
+                SharedPtr<Image> nextImage(new Image(context_));
+                currentImage->nextSibling_ = nextImage;
+                currentImage = nextImage;
+            }
+        }
+        
         // If uncompressed DDS, convert the data to 8bit RGBA as the texture classes can not currently use eg. RGB565 format
         if (compressedFormat_ == CF_RGBA)
         {
             PROFILE(ConvertDDSToRGBA);
 
-            unsigned sourcePixelByteSize = ddsd.ddpfPixelFormat_.dwRGBBitCount_ >> 3;
-            unsigned numPixels = dataSize / sourcePixelByteSize;
+            SharedPtr<Image> currentImage(this);
+            while (currentImage.NotNull())
+            {
+                unsigned sourcePixelByteSize = ddsd.ddpfPixelFormat_.dwRGBBitCount_ >> 3;
+                unsigned numPixels = dataSize / sourcePixelByteSize;
 
-            #define ADJUSTSHIFT(mask, l, r) \
+#define ADJUSTSHIFT(mask, l, r) \
                 if (mask && mask >= 0x100) \
-                { \
-                    while ((mask >> r) >= 0x100) \
+                                { \
+                                                    while ((mask >> r) >= 0x100) \
                         ++r; \
-                } \
-                else if (mask && mask < 0x80) \
-                { \
-                    while ((mask << l) < 0x80) \
+                                } \
+                        else if (mask && mask < 0x80) \
+                                { \
+                                                    while ((mask << l) < 0x80) \
                         ++l; \
-                }
-
-            unsigned rShiftL = 0, gShiftL = 0, bShiftL = 0, aShiftL = 0;
-            unsigned rShiftR = 0, gShiftR = 0, bShiftR = 0, aShiftR = 0;
-            unsigned rMask = ddsd.ddpfPixelFormat_.dwRBitMask_;
-            unsigned gMask = ddsd.ddpfPixelFormat_.dwGBitMask_;
-            unsigned bMask = ddsd.ddpfPixelFormat_.dwBBitMask_;
-            unsigned aMask = ddsd.ddpfPixelFormat_.dwRGBAlphaBitMask_;
-            ADJUSTSHIFT(rMask, rShiftL, rShiftR)
-            ADJUSTSHIFT(gMask, gShiftL, gShiftR)
-            ADJUSTSHIFT(bMask, bShiftL, bShiftR)
-            ADJUSTSHIFT(aMask, aShiftL, aShiftR)
-
-            SharedArrayPtr<unsigned char> rgbaData(new unsigned char[numPixels * 4]);
-            SetMemoryUse(numPixels * 4);
-
-            switch (sourcePixelByteSize)
-            {
-            case 4:
+                                }
+
+                unsigned rShiftL = 0, gShiftL = 0, bShiftL = 0, aShiftL = 0;
+                unsigned rShiftR = 0, gShiftR = 0, bShiftR = 0, aShiftR = 0;
+                unsigned rMask = ddsd.ddpfPixelFormat_.dwRBitMask_;
+                unsigned gMask = ddsd.ddpfPixelFormat_.dwGBitMask_;
+                unsigned bMask = ddsd.ddpfPixelFormat_.dwBBitMask_;
+                unsigned aMask = ddsd.ddpfPixelFormat_.dwRGBAlphaBitMask_;
+                ADJUSTSHIFT(rMask, rShiftL, rShiftR)
+                ADJUSTSHIFT(gMask, gShiftL, gShiftR)
+                ADJUSTSHIFT(bMask, bShiftL, bShiftR)
+                ADJUSTSHIFT(aMask, aShiftL, aShiftR)
+                
+                SharedArrayPtr<unsigned char> rgbaData(new unsigned char[numPixels * 4]);
+                SetMemoryUse(numPixels * 4);
+
+                switch (sourcePixelByteSize)
+                {
+                case 4:
                 {
-                    unsigned* src = (unsigned*)data_.Get();
+                    unsigned* src = (unsigned*)currentImage->data_.Get();
                     unsigned char* dest = rgbaData.Get();
 
                     while (numPixels--)
                     {
                         unsigned pixels = *src++;
-                        *dest++ = (unsigned char)(((pixels & rMask) << rShiftL) >> rShiftR);
-                        *dest++ = (unsigned char)(((pixels & gMask) << gShiftL) >> gShiftR);
-                        *dest++ = (unsigned char)(((pixels & bMask) << bShiftL) >> bShiftR);
-                        *dest++ = (unsigned char)(((pixels & aMask) << aShiftL) >> aShiftR);
+                        *dest++ = ((pixels & rMask) << rShiftL) >> rShiftR;
+                        *dest++ = ((pixels & gMask) << gShiftL) >> gShiftR;
+                        *dest++ = ((pixels & bMask) << bShiftL) >> bShiftR;
+                        *dest++ = ((pixels & aMask) << aShiftL) >> aShiftR;
                     }
                 }
                 break;
 
-            case 3:
+                case 3:
                 {
-                    unsigned char* src = data_.Get();
+                    unsigned char* src = currentImage->data_.Get();
                     unsigned char* dest = rgbaData.Get();
 
                     while (numPixels--)
                     {
                         unsigned pixels = src[0] | (src[1] << 8) | (src[2] << 16);
                         src += 3;
-                        *dest++ = (unsigned char)(((pixels & rMask) << rShiftL) >> rShiftR);
-                        *dest++ = (unsigned char)(((pixels & gMask) << gShiftL) >> gShiftR);
-                        *dest++ = (unsigned char)(((pixels & bMask) << bShiftL) >> bShiftR);
-                        *dest++ = (unsigned char)(((pixels & aMask) << aShiftL) >> aShiftR);
+                        *dest++ = ((pixels & rMask) << rShiftL) >> rShiftR;
+                        *dest++ = ((pixels & gMask) << gShiftL) >> gShiftR;
+                        *dest++ = ((pixels & bMask) << bShiftL) >> bShiftR;
+                        *dest++ = ((pixels & aMask) << aShiftL) >> aShiftR;
                     }
                 }
                 break;
 
-            default:
+                default:
                 {
-                    unsigned short* src = (unsigned short*)data_.Get();
+                    unsigned short* src = (unsigned short*)currentImage->data_.Get();
                     unsigned char* dest = rgbaData.Get();
 
                     while (numPixels--)
                     {
                         unsigned short pixels = *src++;
-                        *dest++ = (unsigned char)(((pixels & rMask) << rShiftL) >> rShiftR);
-                        *dest++ = (unsigned char)(((pixels & gMask) << gShiftL) >> gShiftR);
-                        *dest++ = (unsigned char)(((pixels & bMask) << bShiftL) >> bShiftR);
-                        *dest++ = (unsigned char)(((pixels & aMask) << aShiftL) >> aShiftR);
+                        *dest++ = ((pixels & rMask) << rShiftL) >> rShiftR;
+                        *dest++ = ((pixels & gMask) << gShiftL) >> gShiftR;
+                        *dest++ = ((pixels & bMask) << bShiftL) >> bShiftR;
+                        *dest++ = ((pixels & aMask) << aShiftL) >> aShiftR;
                     }
                 }
                 break;
-            }
+                }
 
-            // Replace with converted data
-            data_ = rgbaData;
+                // Replace with converted data
+                currentImage->data_ = rgbaData;
+                currentImage = currentImage->GetNextSibling();
+            }
         }
     }
     else if (fileID == "\253KTX")

+ 16 - 0
Source/Urho3D/Resource/Image.h

@@ -138,6 +138,12 @@ public:
     bool SaveTGA(const String& fileName) const;
     /// Save in JPG format with compression quality. Return true if successful.
     bool SaveJPG(const String& fileName, int quality) const;
+    /// Whether this texture is detected as a cubemap, only relevant for DDS.
+    bool IsCubemap() const { return cubemap_; }
+    /// Whether this texture has been detected as a volume, only relevant for DDS.
+    bool IsArray() const { return array_; }
+    /// Whether this texture is in sRGB, only relevant for DDS.
+    bool IsSRGB() const { return sRGB_; }
 
     /// Return a 2D pixel color.
     Color GetPixel(int x, int y) const;
@@ -178,6 +184,8 @@ public:
 
     /// Return next mip level by bilinear filtering.
     SharedPtr<Image> GetNextLevel() const;
+    /// Return the next sibling image of an array or cubemap.
+    SharedPtr<Image> GetNextSibling() const { return nextSibling_;  }
     /// Return image converted to 4-component (RGBA) to circumvent modern rendering API's not supporting e.g. the luminance-alpha format.
     SharedPtr<Image> ConvertToRGBA() const;
     /// Return a compressed mip level.
@@ -205,12 +213,20 @@ private:
     unsigned components_;
     /// Number of compressed mip levels.
     unsigned numCompressedLevels_;
+    /// Cubemap status if DDS.
+    bool cubemap_;
+    /// Texture array status if DDS.
+    bool array_;
+    /// Data is sRGB.
+    bool sRGB_;
     /// Compressed format.
     CompressedFormat compressedFormat_;
     /// Pixel data.
     SharedArrayPtr<unsigned char> data_;
     /// Precalculated mip level image.
     SharedPtr<Image> nextLevel_;
+    /// Next texture array or cube map image.
+    SharedPtr<Image> nextSibling_;
 };
 
 }