You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
324 lines
11 KiB
324 lines
11 KiB
#include "../dx.hpp" |
|
#include "file.hpp" |
|
#include <cstdio> |
|
#include <cassert> |
|
|
|
namespace gli{ |
|
namespace detail |
|
{ |
|
static char const FOURCC_DDS[] = {'D', 'D', 'S', ' '}; |
|
|
|
enum dds_cubemap_flag |
|
{ |
|
DDSCAPS2_CUBEMAP = 0x00000200, |
|
DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400, |
|
DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800, |
|
DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000, |
|
DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000, |
|
DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000, |
|
DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000, |
|
DDSCAPS2_VOLUME = 0x00200000 |
|
}; |
|
|
|
enum |
|
{ |
|
DDSCAPS2_CUBEMAP_ALLFACES = DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY | DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ |
|
}; |
|
|
|
enum dds_flag |
|
{ |
|
DDSD_CAPS = 0x00000001, |
|
DDSD_HEIGHT = 0x00000002, |
|
DDSD_WIDTH = 0x00000004, |
|
DDSD_PITCH = 0x00000008, |
|
DDSD_PIXELFORMAT = 0x00001000, |
|
DDSD_MIPMAPCOUNT = 0x00020000, |
|
DDSD_LINEARSIZE = 0x00080000, |
|
DDSD_DEPTH = 0x00800000 |
|
}; |
|
|
|
enum dds_surface_flag |
|
{ |
|
DDSCAPS_COMPLEX = 0x00000008, |
|
DDSCAPS_MIPMAP = 0x00400000, |
|
DDSCAPS_TEXTURE = 0x00001000 |
|
}; |
|
|
|
struct dds_pixel_format |
|
{ |
|
std::uint32_t size; // 32 |
|
dx::ddpf flags; |
|
dx::d3dfmt fourCC; |
|
std::uint32_t bpp; |
|
glm::u32vec4 Mask; |
|
}; |
|
|
|
struct dds_header |
|
{ |
|
std::uint32_t Size; |
|
std::uint32_t Flags; |
|
std::uint32_t Height; |
|
std::uint32_t Width; |
|
std::uint32_t Pitch; |
|
std::uint32_t Depth; |
|
std::uint32_t MipMapLevels; |
|
std::uint32_t Reserved1[11]; |
|
dds_pixel_format Format; |
|
std::uint32_t SurfaceFlags; |
|
std::uint32_t CubemapFlags; |
|
std::uint32_t Reserved2[3]; |
|
}; |
|
|
|
static_assert(sizeof(dds_header) == 124, "DDS Header size mismatch"); |
|
|
|
enum d3d10_resource_dimension |
|
{ |
|
D3D10_RESOURCE_DIMENSION_UNKNOWN = 0, |
|
D3D10_RESOURCE_DIMENSION_BUFFER = 1, |
|
D3D10_RESOURCE_DIMENSION_TEXTURE1D = 2, |
|
D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3, |
|
D3D10_RESOURCE_DIMENSION_TEXTURE3D = 4 |
|
}; |
|
|
|
enum d3d10_resource_misc_flag |
|
{ |
|
D3D10_RESOURCE_MISC_GENERATE_MIPS = 0x01, |
|
D3D10_RESOURCE_MISC_SHARED = 0x02, |
|
D3D10_RESOURCE_MISC_TEXTURECUBE = 0x04, |
|
D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX = 0x10, |
|
D3D10_RESOURCE_MISC_GDI_COMPATIBLE = 0x20, |
|
}; |
|
|
|
enum dds_alpha_mode |
|
{ |
|
DDS_ALPHA_MODE_UNKNOWN = 0x0, |
|
DDS_ALPHA_MODE_STRAIGHT = 0x1, |
|
DDS_ALPHA_MODE_PREMULTIPLIED = 0x2, |
|
DDS_ALPHA_MODE_OPAQUE = 0x3, |
|
DDS_ALPHA_MODE_CUSTOM = 0x4 |
|
}; |
|
|
|
struct dds_header10 |
|
{ |
|
dds_header10() : |
|
Format(dx::DXGI_FORMAT_UNKNOWN), |
|
ResourceDimension(D3D10_RESOURCE_DIMENSION_UNKNOWN), |
|
MiscFlag(0), |
|
ArraySize(0), |
|
AlphaFlags(DDS_ALPHA_MODE_UNKNOWN) |
|
{} |
|
|
|
dx::dxgiFormat Format; |
|
d3d10_resource_dimension ResourceDimension; |
|
std::uint32_t MiscFlag; // D3D10_RESOURCE_MISC_GENERATE_MIPS |
|
std::uint32_t ArraySize; |
|
dds_alpha_mode AlphaFlags; // Should be 0 whenever possible to avoid D3D utility library to fail |
|
}; |
|
|
|
static_assert(sizeof(dds_header10) == 20, "DDS DX10 Extended Header size mismatch"); |
|
|
|
inline target get_target(dds_header const& Header, dds_header10 const& Header10) |
|
{ |
|
if(Header.CubemapFlags & detail::DDSCAPS2_CUBEMAP) |
|
{ |
|
if(Header10.ArraySize > 1) |
|
return TARGET_CUBE_ARRAY; |
|
else |
|
return TARGET_CUBE; |
|
} |
|
else if(Header10.ArraySize > 1) |
|
{ |
|
if(Header.Flags & detail::DDSD_HEIGHT) |
|
return TARGET_2D_ARRAY; |
|
else |
|
return TARGET_1D_ARRAY; |
|
} |
|
else if(Header10.ResourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE1D) |
|
return TARGET_1D; |
|
else if(Header10.ResourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE3D || Header.Flags & detail::DDSD_DEPTH || Header.CubemapFlags & detail::DDSCAPS2_VOLUME) |
|
return TARGET_3D; |
|
else |
|
return TARGET_2D; |
|
} |
|
|
|
// Some formats have multiple fourcc values. This function allows remapping to the default fourcc value of a format |
|
inline dx::d3dfmt remap_four_cc(dx::d3dfmt FourCC) |
|
{ |
|
switch(FourCC) |
|
{ |
|
default: |
|
return FourCC; |
|
case dx::D3DFMT_BC4U: |
|
return dx::D3DFMT_ATI1; |
|
case dx::D3DFMT_BC4S: |
|
return dx::D3DFMT_AT1N; |
|
case dx::D3DFMT_BC5U: |
|
return dx::D3DFMT_ATI2; |
|
case dx::D3DFMT_BC5S: |
|
return dx::D3DFMT_AT2N; |
|
} |
|
} |
|
}//namespace detail |
|
|
|
inline texture load_dds(char const * Data, std::size_t Size) |
|
{ |
|
GLI_ASSERT(Data && (Size >= sizeof(detail::FOURCC_DDS))); |
|
|
|
if(strncmp(Data, detail::FOURCC_DDS, 4) != 0) |
|
return texture(); |
|
std::size_t Offset = sizeof(detail::FOURCC_DDS); |
|
|
|
GLI_ASSERT(Size >= sizeof(detail::dds_header)); |
|
|
|
detail::dds_header const & Header(*reinterpret_cast<detail::dds_header const *>(Data + Offset)); |
|
Offset += sizeof(detail::dds_header); |
|
|
|
detail::dds_header10 Header10; |
|
if((Header.Format.flags & dx::DDPF_FOURCC) && (Header.Format.fourCC == dx::D3DFMT_DX10 || Header.Format.fourCC == dx::D3DFMT_GLI1)) |
|
{ |
|
std::memcpy(&Header10, Data + Offset, sizeof(Header10)); |
|
Offset += sizeof(detail::dds_header10); |
|
} |
|
|
|
dx DX; |
|
|
|
gli::format Format(static_cast<gli::format>(gli::FORMAT_INVALID)); |
|
if((Header.Format.flags & (dx::DDPF_RGB | dx::DDPF_ALPHAPIXELS | dx::DDPF_ALPHA | dx::DDPF_YUV | dx::DDPF_LUMINANCE)) && Format == static_cast<format>(gli::FORMAT_INVALID) && Header.Format.bpp != 0) |
|
{ |
|
switch(Header.Format.bpp) |
|
{ |
|
default: |
|
GLI_ASSERT(0); |
|
break; |
|
case 8: |
|
{ |
|
if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_RG4_UNORM_PACK8).Mask))) |
|
Format = FORMAT_RG4_UNORM_PACK8; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_L8_UNORM_PACK8).Mask))) |
|
Format = FORMAT_L8_UNORM_PACK8; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_A8_UNORM_PACK8).Mask))) |
|
Format = FORMAT_A8_UNORM_PACK8; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_R8_UNORM_PACK8).Mask))) |
|
Format = FORMAT_R8_UNORM_PACK8; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_RG3B2_UNORM_PACK8).Mask))) |
|
Format = FORMAT_RG3B2_UNORM_PACK8; |
|
else |
|
GLI_ASSERT(0); |
|
break; |
|
} |
|
case 16: |
|
{ |
|
if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_RGBA4_UNORM_PACK16).Mask))) |
|
Format = FORMAT_RGBA4_UNORM_PACK16; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_BGRA4_UNORM_PACK16).Mask))) |
|
Format = FORMAT_BGRA4_UNORM_PACK16; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_R5G6B5_UNORM_PACK16).Mask))) |
|
Format = FORMAT_R5G6B5_UNORM_PACK16; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_B5G6R5_UNORM_PACK16).Mask))) |
|
Format = FORMAT_B5G6R5_UNORM_PACK16; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_RGB5A1_UNORM_PACK16).Mask))) |
|
Format = FORMAT_RGB5A1_UNORM_PACK16; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_BGR5A1_UNORM_PACK16).Mask))) |
|
Format = FORMAT_BGR5A1_UNORM_PACK16; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_LA8_UNORM_PACK8).Mask))) |
|
Format = FORMAT_LA8_UNORM_PACK8; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_RG8_UNORM_PACK8).Mask))) |
|
Format = FORMAT_RG8_UNORM_PACK8; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_L16_UNORM_PACK16).Mask))) |
|
Format = FORMAT_L16_UNORM_PACK16; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_A16_UNORM_PACK16).Mask))) |
|
Format = FORMAT_A16_UNORM_PACK16; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_R16_UNORM_PACK16).Mask))) |
|
Format = FORMAT_R16_UNORM_PACK16; |
|
else |
|
GLI_ASSERT(0); |
|
break; |
|
} |
|
case 24: |
|
{ |
|
if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_RGB8_UNORM_PACK8).Mask))) |
|
Format = FORMAT_RGB8_UNORM_PACK8; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_BGR8_UNORM_PACK8).Mask))) |
|
Format = FORMAT_BGR8_UNORM_PACK8; |
|
else |
|
GLI_ASSERT(0); |
|
break; |
|
} |
|
case 32: |
|
{ |
|
if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_BGR8_UNORM_PACK32).Mask))) |
|
Format = FORMAT_BGR8_UNORM_PACK32; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_BGRA8_UNORM_PACK8).Mask))) |
|
Format = FORMAT_BGRA8_UNORM_PACK8; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_RGBA8_UNORM_PACK8).Mask))) |
|
Format = FORMAT_RGBA8_UNORM_PACK8; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_RGB10A2_UNORM_PACK32).Mask))) |
|
Format = FORMAT_RGB10A2_UNORM_PACK32; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_LA16_UNORM_PACK16).Mask))) |
|
Format = FORMAT_LA16_UNORM_PACK16; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_RG16_UNORM_PACK16).Mask))) |
|
Format = FORMAT_RG16_UNORM_PACK16; |
|
else if(glm::all(glm::equal(Header.Format.Mask, DX.translate(FORMAT_R32_SFLOAT_PACK32).Mask))) |
|
Format = FORMAT_R32_SFLOAT_PACK32; |
|
else |
|
GLI_ASSERT(0); |
|
break; |
|
} |
|
} |
|
} |
|
else if((Header.Format.flags & dx::DDPF_FOURCC) && (Header.Format.fourCC != dx::D3DFMT_DX10) && (Header.Format.fourCC != dx::D3DFMT_GLI1) && (Format == static_cast<format>(gli::FORMAT_INVALID))) |
|
{ |
|
dx::d3dfmt const FourCC = detail::remap_four_cc(Header.Format.fourCC); |
|
Format = DX.find(FourCC); |
|
} |
|
else if(Header.Format.fourCC == dx::D3DFMT_DX10 || Header.Format.fourCC == dx::D3DFMT_GLI1) |
|
Format = DX.find(Header.Format.fourCC, Header10.Format); |
|
|
|
GLI_ASSERT(Format != static_cast<format>(gli::FORMAT_INVALID)); |
|
|
|
size_t const MipMapCount = (Header.Flags & detail::DDSD_MIPMAPCOUNT) ? Header.MipMapLevels : 1; |
|
size_t FaceCount = 1; |
|
if(Header.CubemapFlags & detail::DDSCAPS2_CUBEMAP) |
|
FaceCount = int(glm::bitCount(Header.CubemapFlags & detail::DDSCAPS2_CUBEMAP_ALLFACES)); |
|
|
|
size_t DepthCount = 1; |
|
if(Header.CubemapFlags & detail::DDSCAPS2_VOLUME) |
|
DepthCount = Header.Depth; |
|
|
|
texture Texture( |
|
get_target(Header, Header10), Format, |
|
texture::extent_type(Header.Width, Header.Height, DepthCount), |
|
std::max<texture::size_type>(Header10.ArraySize, 1), FaceCount, MipMapCount); |
|
|
|
std::size_t const SourceSize = Offset + Texture.size(); |
|
GLI_ASSERT(SourceSize == Size); |
|
|
|
std::memcpy(Texture.data(), Data + Offset, Texture.size()); |
|
|
|
return Texture; |
|
} |
|
|
|
inline texture load_dds(char const * Filename) |
|
{ |
|
FILE* File = detail::open_file(Filename, "rb"); |
|
if(!File) |
|
return texture(); |
|
|
|
long Beg = std::ftell(File); |
|
std::fseek(File, 0, SEEK_END); |
|
long End = std::ftell(File); |
|
std::fseek(File, 0, SEEK_SET); |
|
|
|
std::vector<char> Data(static_cast<std::size_t>(End - Beg)); |
|
|
|
std::fread(&Data[0], 1, Data.size(), File); |
|
std::fclose(File); |
|
|
|
return load_dds(&Data[0], Data.size()); |
|
} |
|
|
|
inline texture load_dds(std::string const & Filename) |
|
{ |
|
return load_dds(Filename.c_str()); |
|
} |
|
}//namespace gli
|
|
|