以下でほぼ全部です。キューブマップ、ミップマップ、圧縮/非圧縮すべて対応しています。エラーチェックはちゃんとしていないので壊れたファイルや未対応の形式でバッファーオーバーランなどの危険があるかもしれません。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct DDSHeader { | |
uint32_t h3[3]; | |
int h, w; | |
uint32_t h2[2]; | |
int mipCnt; | |
uint32_t h13[13]; | |
uint32_t fourcc, bitsPerPixel, rMask, gMask, bMask, aMask, caps1, caps2; | |
bool IsCubeMap() const { return caps2 == 0xFE00; } | |
int GetArraySize() const { return IsCubeMap() ? 6 : 1; } | |
int GetMipCnt() const { return std::max(mipCnt, 1); } | |
}; | |
static void ArrangeRawDDS(void* img, int size) | |
{ | |
const DDSHeader* hdr = (DDSHeader*)img; | |
DWORD rShift, gShift, bShift, aShift; | |
_BitScanForward(&rShift, hdr->rMask); | |
_BitScanForward(&gShift, hdr->gMask); | |
_BitScanForward(&bShift, hdr->bMask); | |
_BitScanForward(&aShift, hdr->aMask); | |
std::for_each((uint32_t*)img + 128 / 4, (uint32_t*)img + size / 4, [&](uint32_t& im) { | |
im = ((hdr->aMask & im) >> aShift << 24) + ((hdr->bMask & im) >> bShift << 16) + ((hdr->gMask & im) >> gShift << 8) + ((hdr->rMask & im) >> rShift); | |
} ); | |
} | |
static ComPtr<ID3D11ShaderResourceView> LoadDDSTexture(const char* name, ivec2& texSize) | |
{ | |
int size; | |
ComPtr<ID3D11ShaderResourceView> srv; | |
void* img = LoadFile(name, &size); | |
if (!img) { | |
aflog("LoadDDSTexture failed! %s", name); | |
return 0; | |
} | |
const DDSHeader* hdr = (DDSHeader*)img; | |
DXGI_FORMAT format; | |
int (*pitchCalcurator)(int, int) = nullptr; | |
switch (hdr->fourcc) { | |
case 0x31545844: //'1TXD': | |
format = DXGI_FORMAT_BC1_UNORM; | |
pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 8; }; | |
break; | |
case 0x33545844: //'3TXD': | |
format = DXGI_FORMAT_BC2_UNORM; | |
pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 16; }; | |
break; | |
case 0x35545844: //'5TXD': | |
format = DXGI_FORMAT_BC3_UNORM; | |
pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 16; }; | |
break; | |
default: | |
ArrangeRawDDS(img, size); | |
format = DXGI_FORMAT_R8G8B8A8_UNORM; | |
pitchCalcurator = [](int w, int h) { return w * h * 4; }; | |
break; | |
} | |
texSize.x = hdr->w; | |
texSize.y = hdr->h; | |
int arraySize = hdr->GetArraySize(); | |
int mipCnt = hdr->GetMipCnt(); | |
CD3D11_TEXTURE2D_DESC desc(format, hdr->w, hdr->h, arraySize, hdr->GetMipCnt(), D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DEFAULT, 0, 1, 0, hdr->IsCubeMap() ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0); | |
CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(hdr->IsCubeMap() ? D3D_SRV_DIMENSION_TEXTURECUBE : D3D_SRV_DIMENSION_TEXTURE2D, desc.Format, 0, -1); | |
std::vector<D3D11_SUBRESOURCE_DATA> r; | |
int offset = 128; | |
for (int a = 0; a < arraySize; a++) { | |
for (int m = 0; m < mipCnt; m++) { | |
int w = std::max(1, hdr->w >> m); | |
int h = std::max(1, hdr->h >> m); | |
r.push_back({ (char*)img + offset, (uint32_t)pitchCalcurator(w, 1), 0 }); | |
offset += pitchCalcurator(w, h); | |
} | |
} | |
ComPtr<ID3D11Texture2D> tex; | |
deviceMan11.GetDevice()->CreateTexture2D(&desc, &r[0], &tex); | |
deviceMan11.GetDevice()->CreateShaderResourceView(tex.Get(), &srvDesc, &srv); | |
free(img); | |
return srv; | |
} |
ID3D11Texture2DとDDSファイルは共にキューブマップやミップマップの複数のイメージを内包します。よって、DDSからそれらを含むID3D11Texture2Dを生成するときにD3D11_SUBRESOURCE_DATAの配列でその複数イメージを指定します。
ミップマップ入りキューブマップ等になるとD3D11_SUBRESOURCE_DATAの順で悩みそうですが、これはIntroduction To Textures in Direct3D 11が詳しいです。更に都合のよいことに、D3D11_SUBRESOURCE_DATAとDDSの画像の配列順は同じになっているようです。
また、各画像の開始位置を正確にD3D11_SUBRESOURCE_DATAに設定しなければいけませんが、テクスチャ用の DDS ファイルのレイアウトと題されたページが参考になります。
やってみると意外と短いコードで出来たので紹介してみました。