From 30a50549c86190ef2df3c55af79318fe5fdd913a Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 15 Oct 2020 18:42:29 +0200 Subject: [PATCH] added support for BC6H, BC7, ETC1, ETC2_RGB and ETC2_RGBA textures --- FModel/FModel.csproj | 2 + FModel/PakReader/Textures/BC/Detex.cs | 106 +++++ .../BC/DetexCompressedTextureFormatIndex.cs | 39 ++ .../PakReader/Textures/BC/DetexPixelFormat.cs | 361 ++++++++++++++++++ .../Textures/BC/DetexTextureFormat.cs | 119 ++++++ FModel/PakReader/Textures/TextureDecoder.cs | 31 ++ FModel/Resources/Detex.dll | Bin 0 -> 50176 bytes 7 files changed, 658 insertions(+) create mode 100644 FModel/PakReader/Textures/BC/Detex.cs create mode 100644 FModel/PakReader/Textures/BC/DetexCompressedTextureFormatIndex.cs create mode 100644 FModel/PakReader/Textures/BC/DetexPixelFormat.cs create mode 100644 FModel/PakReader/Textures/BC/DetexTextureFormat.cs create mode 100644 FModel/Resources/Detex.dll diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj index 50d7786d..04e88c80 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -127,6 +127,8 @@ + + diff --git a/FModel/PakReader/Textures/BC/Detex.cs b/FModel/PakReader/Textures/BC/Detex.cs new file mode 100644 index 00000000..bcd032aa --- /dev/null +++ b/FModel/PakReader/Textures/BC/Detex.cs @@ -0,0 +1,106 @@ +using System; +using System.IO; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +namespace PakReader.Textures.BC +{ + public static class Detex + { + private const string DETEX_DLL_NAME = "Detex.dll"; + + static Detex() + { + PrepareDllFile(); + } + + [StructLayout(LayoutKind.Sequential)] + private unsafe struct detexTexture + { + public uint format; + public byte* data; + public int width; + public int height; + public int width_in_blocks; + public int height_in_blocks; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] DecodeDetexLinear(byte[] inp, int width, int height, bool isFloat, DetexTextureFormat inputFormat, + DetexPixelFormat outputPixelFormat, int blockSizeX = 4, int blockSizeY = 4) + { + var dst = new byte[width * height * (isFloat ? 16 : 4)]; + DecodeDetexLinear(inp, dst, width, height, inputFormat, outputPixelFormat, blockSizeX, blockSizeY); + return dst; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool DecodeDetexLinear(byte[] inp, byte[] dst, int width, int height, DetexTextureFormat inputFormat, DetexPixelFormat outputPixelFormat, int blockSizeX = 4, int blockSizeY = 4) + { + unsafe + { + detexTexture tex; + tex.format = (uint) inputFormat; + tex.data = (byte*) Unsafe.AsPointer(ref inp[0]); + tex.width = width; + tex.height = height; + tex.width_in_blocks = width / 4; + tex.height_in_blocks = height / 4; + return detexDecompressTextureLinear(&tex, (byte*) Unsafe.AsPointer(ref dst[0]), + (uint) outputPixelFormat); + } + } + + public static byte[] DecodeBC6H(byte[] inp, int width, int height) + { + const int PIXEL_SIZE = 16; + var dst = new byte[width * height * PIXEL_SIZE]; + + DecodeDetexLinear(inp, dst, width, height, DetexTextureFormat.DETEX_TEXTURE_FORMAT_BPTC_FLOAT, + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBX8); + + return dst; + } + + [DllImport(DETEX_DLL_NAME)] + private static extern unsafe bool detexDecompressTextureLinear(detexTexture* texture, byte* pixelBuffer, + uint pixelFormat); + + private static void PrepareDllFile() + { + using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("FModel.Resources.Detex.dll"); + if (stream == null) + throw new MissingManifestResourceException("Couldn't find Detex.dll in Embedded Resources"); + var ba = new byte[(int) stream.Length]; + stream.Read(ba, 0, (int) stream.Length); + + bool fileOk; + var dllFile = DETEX_DLL_NAME; + + using (var sha1 = new SHA1CryptoServiceProvider()) + { + var fileHash = BitConverter.ToString(sha1.ComputeHash(ba)).Replace("-", string.Empty); + + if (File.Exists(dllFile)) + { + var bb = File.ReadAllBytes(dllFile); + var fileHash2 = BitConverter.ToString(sha1.ComputeHash(bb)).Replace("-", string.Empty); + + fileOk = fileHash == fileHash2; + } + else + { + fileOk = false; + } + } + + if (!fileOk) + { + File.WriteAllBytes(dllFile, ba); + } + } + } +} \ No newline at end of file diff --git a/FModel/PakReader/Textures/BC/DetexCompressedTextureFormatIndex.cs b/FModel/PakReader/Textures/BC/DetexCompressedTextureFormatIndex.cs new file mode 100644 index 00000000..88d1b30c --- /dev/null +++ b/FModel/PakReader/Textures/BC/DetexCompressedTextureFormatIndex.cs @@ -0,0 +1,39 @@ +namespace PakReader.Textures.BC +{ + internal enum DetexCompressedTextureFormatIndex : uint + { + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_UNCOMPRESSED = 0, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC1 = 1, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_DXT1 = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC1, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_S3TC = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC1, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC1A, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_DXT1A = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC1A, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC2, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_DXT3 = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC2, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC3, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_DXT5 = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC3, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_RGTC1, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC4_UNORM = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_RGTC1, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_SIGNED_RGTC1, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC4_SNORM = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_SIGNED_RGTC1, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_RGTC2, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC5_UNORM = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_RGTC2, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_SIGNED_RGTC2, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC5_SNORM = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_SIGNED_RGTC2, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BPTC_FLOAT, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC6H_UF16 = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BPTC_FLOAT, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BPTC_SIGNED_FLOAT, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC6H_SF16 = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BPTC_SIGNED_FLOAT, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BPTC, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC7 = DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BPTC, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_ETC1, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_ETC2, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_ETC2_PUNCHTHROUGH, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_ETC2_EAC, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_EAC_R11, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_EAC_SIGNED_R11, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_EAC_RG11, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_EAC_SIGNED_RG11, + DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_ASTC_4X4, + } +} \ No newline at end of file diff --git a/FModel/PakReader/Textures/BC/DetexPixelFormat.cs b/FModel/PakReader/Textures/BC/DetexPixelFormat.cs new file mode 100644 index 00000000..9dcc2f0b --- /dev/null +++ b/FModel/PakReader/Textures/BC/DetexPixelFormat.cs @@ -0,0 +1,361 @@ +namespace PakReader.Textures.BC +{ + public enum DetexPixelFormat : uint + { + /* The format has 16-bit components. */ + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT = 0x1, + + /* The format has 32-bit components. */ + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT = 0x2, + + /* The format has an alpha component. */ + DETEX_PIXEL_FORMAT_ALPHA_COMPONENT_BIT = 0x4, + + /* The sequential component order is RGB. */ + DETEX_PIXEL_FORMAT_RGB_COMPONENT_ORDER_BIT = 0x0, + + /* The sequential component order is BGR. */ + DETEX_PIXEL_FORMAT_BGR_COMPONENT_ORDER_BIT = 0x8, + + /* The format has one component. */ + DETEX_PIXEL_FORMAT_ONE_COMPONENT_BITS = 0x0, + + /* The format has two components. */ + DETEX_PIXEL_FORMAT_TWO_COMPONENTS_BITS = 0x10, + + /* The format has three components. */ + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS = 0x20, + + /* The format has four components. */ + DETEX_PIXEL_FORMAT_FOUR_COMPONENTS_BITS = 0x30, + + /* The format is stored as 8-bit pixels. */ + DETEX_PIXEL_FORMAT_8BIT_PIXEL_BITS = 0x000, + + /* The format is stored as 16-bit pixels. */ + DETEX_PIXEL_FORMAT_16BIT_PIXEL_BITS = 0x100, + + /* The format is stored as 24-bit pixels. */ + DETEX_PIXEL_FORMAT_24BIT_PIXEL_BITS = 0x200, + + /* The format is stored as 32-bit pixels. */ + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS = 0x300, + + /* The format is stored as 48-bit pixels. */ + DETEX_PIXEL_FORMAT_48BIT_PIXEL_BITS = 0x500, + + /* The format is stored as 64-bit pixels. */ + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS = 0x700, + + /* The format is stored as 96-bit pixels. */ + DETEX_PIXEL_FORMAT_96BIT_PIXEL_BITS = 0xB00, + + /* The format is stored as 128-bit pixels. */ + DETEX_PIXEL_FORMAT_128BIT_PIXEL_BITS = 0xF00, + + /* The format has signed integer components. */ + DETEX_PIXEL_FORMAT_SIGNED_BIT = 0x1000, + + /* The format has (half-)float components. */ + DETEX_PIXEL_FORMAT_FLOAT_BIT = 0x2000, + + /* The fomat is HDR (high dynamic range). */ + DETEX_PIXEL_FORMAT_HDR_BIT = 0x4000, + + DETEX_PIXEL_FORMAT_RGBA8 = ( + DETEX_PIXEL_FORMAT_ALPHA_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_FOUR_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_BGRA8 = ( + DETEX_PIXEL_FORMAT_ALPHA_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_BGR_COMPONENT_ORDER_BIT | + DETEX_PIXEL_FORMAT_FOUR_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_RGBX8 = ( + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_BGRX8 = ( + DETEX_PIXEL_FORMAT_BGR_COMPONENT_ORDER_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_RGB8 = ( + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_24BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_BGR8 = ( + DETEX_PIXEL_FORMAT_BGR_COMPONENT_ORDER_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_24BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_R8 = ( + DETEX_PIXEL_FORMAT_ONE_COMPONENT_BITS | + DETEX_PIXEL_FORMAT_8BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_SIGNED_R8 = ( + DETEX_PIXEL_FORMAT_ONE_COMPONENT_BITS | + DETEX_PIXEL_FORMAT_8BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_SIGNED_BIT + ), + + DETEX_PIXEL_FORMAT_RG8 = ( + DETEX_PIXEL_FORMAT_TWO_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_16BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_SIGNED_RG8 = ( + DETEX_PIXEL_FORMAT_TWO_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_16BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_SIGNED_BIT + ), + + DETEX_PIXEL_FORMAT_R16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ONE_COMPONENT_BITS | + DETEX_PIXEL_FORMAT_16BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_SIGNED_R16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ONE_COMPONENT_BITS | + DETEX_PIXEL_FORMAT_16BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_SIGNED_BIT + ), + + DETEX_PIXEL_FORMAT_RG16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_TWO_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_SIGNED_RG16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_TWO_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_SIGNED_BIT + ), + + DETEX_PIXEL_FORMAT_RGB16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_48BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_RGBX16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_RGBA16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ALPHA_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_FOUR_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS + ), + + DETEX_PIXEL_FORMAT_FLOAT_R16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ONE_COMPONENT_BITS | + DETEX_PIXEL_FORMAT_16BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_R16_HDR = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ONE_COMPONENT_BITS | + DETEX_PIXEL_FORMAT_16BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RG16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_TWO_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RG16_HDR = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_TWO_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGBX16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGBX16_HDR = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGBA16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ALPHA_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_FOUR_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGBA16_HDR = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ALPHA_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_FOUR_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGB16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_48BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGB16_HDR = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_48BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_BGRX16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_BGR_COMPONENT_ORDER_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_BGRX16_HDR = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_BGR_COMPONENT_ORDER_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_SIGNED_FLOAT_RGBX16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_SIGNED_BIT | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_SIGNED_FLOAT_BGRX16 = ( + DETEX_PIXEL_FORMAT_16BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_BGR_COMPONENT_ORDER_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_SIGNED_BIT | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_R32 = ( + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ONE_COMPONENT_BITS | + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_R32_HDR = ( + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ONE_COMPONENT_BITS | + DETEX_PIXEL_FORMAT_32BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RG32 = ( + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_TWO_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RG32_HDR = ( + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_TWO_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_64BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGB32 = ( + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_96BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGB32_HDR = ( + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_96BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGBX32 = ( + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_128BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGBX32_HDR = ( + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_THREE_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_128BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGBA32 = ( + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ALPHA_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_FOUR_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_128BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT + ), + + DETEX_PIXEL_FORMAT_FLOAT_RGBA32_HDR = ( + DETEX_PIXEL_FORMAT_32BIT_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ALPHA_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_FOUR_COMPONENTS_BITS | + DETEX_PIXEL_FORMAT_128BIT_PIXEL_BITS | + DETEX_PIXEL_FORMAT_FLOAT_BIT | + DETEX_PIXEL_FORMAT_HDR_BIT + ), + + DETEX_PIXEL_FORMAT_A8 = ( + DETEX_PIXEL_FORMAT_ALPHA_COMPONENT_BIT | + DETEX_PIXEL_FORMAT_ONE_COMPONENT_BITS | + DETEX_PIXEL_FORMAT_8BIT_PIXEL_BITS + ), + } +} \ No newline at end of file diff --git a/FModel/PakReader/Textures/BC/DetexTextureFormat.cs b/FModel/PakReader/Textures/BC/DetexTextureFormat.cs new file mode 100644 index 00000000..23247a13 --- /dev/null +++ b/FModel/PakReader/Textures/BC/DetexTextureFormat.cs @@ -0,0 +1,119 @@ +namespace PakReader.Textures.BC +{ + public enum DetexTextureFormat : uint + { + DETEX_TEXTURE_FORMAT_PIXEL_FORMAT_MASK = 0x0000FFFF, + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT = 0x00800000, + + DETEX_TEXTURE_FORMAT_BC1 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC1 << 24) | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBX8 + ), + + DETEX_TEXTURE_FORMAT_BC1A = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC1A << 24) | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8 + ), + + DETEX_TEXTURE_FORMAT_BC2 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC2 << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8 + ), + + DETEX_TEXTURE_FORMAT_BC3 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BC3 << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8 + ), + + DETEX_TEXTURE_FORMAT_RGTC1 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_RGTC1 << 24) | + DetexPixelFormat.DETEX_PIXEL_FORMAT_R8 + ), + + DETEX_TEXTURE_FORMAT_SIGNED_RGTC1 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_SIGNED_RGTC1 << 24) | + DetexPixelFormat.DETEX_PIXEL_FORMAT_SIGNED_R16 + ), + + DETEX_TEXTURE_FORMAT_RGTC2 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_RGTC2 << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RG8 + ), + + DETEX_TEXTURE_FORMAT_SIGNED_RGTC2 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_SIGNED_RGTC2 << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_SIGNED_RG16 + ), + + DETEX_TEXTURE_FORMAT_BPTC_FLOAT = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BPTC_FLOAT << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_FLOAT_RGBX16 + ), + + DETEX_TEXTURE_FORMAT_BPTC_SIGNED_FLOAT = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BPTC_SIGNED_FLOAT << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_SIGNED_FLOAT_RGBX16 + ), + + DETEX_TEXTURE_FORMAT_BPTC = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_BPTC << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8 + ), + + DETEX_TEXTURE_FORMAT_ETC1 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_ETC1 << 24) | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBX8 + ), + + DETEX_TEXTURE_FORMAT_ETC2 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_ETC2 << 24) | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBX8 + ), + + DETEX_TEXTURE_FORMAT_ETC2_PUNCHTHROUGH = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_ETC2_PUNCHTHROUGH << 24) | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8 + ), + + DETEX_TEXTURE_FORMAT_ETC2_EAC = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_ETC2_EAC << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8 + ), + + DETEX_TEXTURE_FORMAT_EAC_R11 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_EAC_R11 << 24) | + DetexPixelFormat.DETEX_PIXEL_FORMAT_R16 + ), + + DETEX_TEXTURE_FORMAT_EAC_SIGNED_R11 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_EAC_SIGNED_R11 << 24) | + DetexPixelFormat.DETEX_PIXEL_FORMAT_SIGNED_R16 + ), + + DETEX_TEXTURE_FORMAT_EAC_RG11 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_EAC_RG11 << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RG16 + ), + + DETEX_TEXTURE_FORMAT_EAC_SIGNED_RG11 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_EAC_SIGNED_RG11 << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_SIGNED_RG16 + ), + + DETEX_TEXTURE_FORMAT_ASTC_4X4 = ( + (DetexCompressedTextureFormatIndex.DETEX_COMPRESSED_TEXTURE_FORMAT_INDEX_ASTC_4X4 << 24) | + DETEX_TEXTURE_FORMAT_128BIT_BLOCK_BIT | + DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8 + ), + } +} \ No newline at end of file diff --git a/FModel/PakReader/Textures/TextureDecoder.cs b/FModel/PakReader/Textures/TextureDecoder.cs index d929915c..6c0a557d 100644 --- a/FModel/PakReader/Textures/TextureDecoder.cs +++ b/FModel/PakReader/Textures/TextureDecoder.cs @@ -50,6 +50,37 @@ namespace PakReader.Textures data = sequence; colorType = SKColorType.RgbaF16; break; + case EPixelFormat.PF_BC7: + data = Detex.DecodeDetexLinear(sequence, width, height, isFloat: false, + inputFormat: DetexTextureFormat.DETEX_TEXTURE_FORMAT_BPTC, + outputPixelFormat: DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8); + colorType = SKColorType.Rgba8888; + break; + case EPixelFormat.PF_BC6H: + data = Detex.DecodeDetexLinear(sequence, width, height, isFloat: true, + inputFormat: DetexTextureFormat.DETEX_TEXTURE_FORMAT_BPTC_FLOAT, + outputPixelFormat: DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBX8); // Not sure whether that works, would actually be DETEX_PIXEL_FORMAT_FLOAT_RGBX32 + data = Detex.DecodeBC6H(sequence, width, height); + colorType = SKColorType.Rgb888x; + break; + case EPixelFormat.PF_ETC1: + data = Detex.DecodeDetexLinear(sequence, width, height, isFloat: false, + inputFormat: DetexTextureFormat.DETEX_TEXTURE_FORMAT_ETC1, + outputPixelFormat: DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8); + colorType = SKColorType.Rgba8888; + break; + case EPixelFormat.PF_ETC2_RGB: + data = Detex.DecodeDetexLinear(sequence, width, height, isFloat: false, + inputFormat: DetexTextureFormat.DETEX_TEXTURE_FORMAT_ETC2, + outputPixelFormat: DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8); + colorType = SKColorType.Rgba8888; + break; + case EPixelFormat.PF_ETC2_RGBA: + data = Detex.DecodeDetexLinear(sequence, width, height, isFloat: false, + inputFormat: DetexTextureFormat.DETEX_TEXTURE_FORMAT_ETC2_EAC, + outputPixelFormat: DetexPixelFormat.DETEX_PIXEL_FORMAT_RGBA8); + colorType = SKColorType.Rgba8888; + break; default: throw new NotImplementedException($"Cannot decode {format} format"); } diff --git a/FModel/Resources/Detex.dll b/FModel/Resources/Detex.dll new file mode 100644 index 0000000000000000000000000000000000000000..a05872ba1ab3fd19d07f2144055e707a546e045e GIT binary patch literal 50176 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 Qi-6MnNbV!V_EbLqAIvO^UH||9 literal 0 HcmV?d00001