generating mipmaps for texture arrays

Nov 7, 2012 at 10:14 AM

hi,

i have code to generate texture array (based on your suggestions in another thread) and it's working but i can't generate mipmaps for it, how should i use GenerateMipMaps for texture arrays? generatemipmaps calls Initialize2D() with harcoded value 1 as array size, is this ok? this is how i am doing it right now:

 

ScratchImage** originalImages = new ScratchImage*[6];

   
    for(int i = 0; i < 5; i++)
    {
        TexMetadata metaData;

        originalImages[i] = new ScratchImage();
        hr = LoadFromDDSFile(paths[i].c_str(), DDS_FLAGS_LEGACY_DWORD, &metaData, *originalImages[i]);
        if(FAILED(hr)) assert(false);
    }

    ///// TODO: check if textures have same size and format and if not convert them

    Image tmpImages[5];
    for(int i = 0; i < 5; ++i)
    {
        Image img = originalImages[i]->GetImages()[0];
        tmpImages[i] = img;
    }

    size_t tMips=10;
    DWORD dwFilter = TEX_FILTER_DEFAULT;

    ScratchImage outputImage;

    hr = outputImage.InitializeArrayFromImages(tmpImages, 5);
    if(FAILED(hr)) assert(false);
    
    GenerateMipMaps(tmpImages[0], dwFilter, tMips, outputImage);

    hr = SaveToDDSFile(tmpImages, outputImage.GetImageCount(), outputImage.GetMetadata(), DDS_FLAGS_NONE,  L"file.dds");

Coordinator
Nov 26, 2012 at 8:47 PM
Edited Nov 26, 2012 at 9:07 PM

The second form of GenerateMipMaps can process an array of images into an array of images with mipmaps (which is what happens with a cubemap). I think you need something like this:

DWORD ddsFlags = DDS_FLAGS_LEGACY_DWORD;

const size_t NUM_IMAGES = 6;
ScratchImage* originalImages = new ScratchImage[NUM_IMAGES];

for(size_t i = 0; i < NUM_IMAGES; i++)
{
   hr = LoadFromDDSFile(paths[i].c_str(), ddsFlags, nullptr, originalImages[i]);
    if(FAILED(hr)) ...
}

///// TODO: check if textures have same size and format and if not convert them

ScratchImage arrayImage;
hr = arrayImage.InitializeArrayFromImages(originalImages, NUM_IMAGES);
if(FAILED(hr)) ...

delete [] originalImages;

DWORD dwFilter = TEX_FILTER_DEFAULT;

ScratchImage outputImage;
hr = GenerateMipMaps(arrayImage.GetImages(), arrayImage.GetImageCount(), arrayImage.GetMetadata(), dwFilter, 0, outputImage );
if(FAILED(hr)) ...

hr = SaveToDDSFile(outputImage.GetImages(), outputImage.GetImageCount(), outputImage.GetMetadata(), DDS_FLAGS_NONE, L"file.dds");
if(FAILED(hr)) ...

A few comments:

  • Are your DDS source files all old-school DDS files of 24bpp images? DDS_FLAGS_LEGACY_DWORD doesn't result in the correct images for standard DDS files so you shouldn't hard-code this.
  • ScratchImage is a fairly robust container, so you don't need to make an array of pointers to new instances. Just use an array of ScratchImages and load into the elements of it.
  • Why are you loading from DDS files originally? Do you not have more traditional image sources such as JPG, BMP, etc? It's fine to use DDS as a source, but normally you'd use some standard bitmap format as your art source
  • By using a hard-coded value of tMips=10, you are assuming your source images are a particular size. You use '0' to indicate 'automatically generate all mipmap levels'
  • size_t is your friend for x86/x64 portable values like loop counts.

Also, remember that the various "Initialize2D", "InitalizeArrayFromImages", etc. are custom helpers for ScratchImage. The main workhorse function is:

    HRESULT Initialize( _In_ const TexMetadata& mdata );

Not to confuse things further, but you should consider using std::unique_ptr to make sure your heap-allocated temporaries get cleaned up correctly without having to do manual delete [] originalImages in every error control path

#include <memory>

DWORD ddsFlags = DDS_FLAGS_LEGACY_DWORD;

const size_t NUM_IMAGES = 6;
std::unique_ptr originalImages( new ScratchImage[6] );
    
    for(size_t i = 0; i < NUM_IMAGES; i++)
    {
        hr = LoadFromDDSFile(paths[i].c_str(), ddsFlags, nullptr, originalImages[i]);
        if(FAILED(hr)) ...
    }

    ScratchImage arrayImage;
    hr = arrayImage.InitializeArrayFromImages( originalImages, NUM_IMAGES );