325 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			325 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #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
 | 
