From 5ef1e585c60b292cd354a202ce4e8b76e10390fa Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 22 Feb 2020 01:34:27 +0100 Subject: [PATCH] ObjWriting: Implement writing textures as DDS files --- src/ObjCommon/Image/DdsTypes.h | 107 +++++++++++ src/ObjCommon/Image/ImageFormat.cpp | 32 +++- src/ObjCommon/Image/ImageFormat.h | 24 ++- src/ObjCommon/Image/Texture.cpp | 79 +++++++- src/ObjCommon/Image/Texture.h | 44 ++++- src/ObjWriting/Image/DdsWriter.cpp | 271 +++++++++++++++++++++++++++- src/ObjWriting/Image/DdsWriter.h | 2 +- 7 files changed, 536 insertions(+), 23 deletions(-) create mode 100644 src/ObjCommon/Image/DdsTypes.h diff --git a/src/ObjCommon/Image/DdsTypes.h b/src/ObjCommon/Image/DdsTypes.h new file mode 100644 index 00000000..9ffe589e --- /dev/null +++ b/src/ObjCommon/Image/DdsTypes.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include + +namespace dds +{ + constexpr uint32_t MakeFourCc(const char c0, const char c1, const char c2, const char c3) + { + return static_cast(c0) + | static_cast(c1) << 8 + | static_cast(c2) << 16 + | static_cast(c3) << 24; + } + + enum DDP_FLAGS + { + DDPF_ALPHAPIXELS = 0x1, + DDPF_ALPHA = 0x2, + DDPF_FOURCC = 0x4, + DDPF_RGB = 0x40, + DDPF_YUV = 0x200, + DDPF_LUMINANCE = 0x20000 + }; + + struct DDS_PIXELFORMAT + { + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwFourCC; + uint32_t dwRGBBitCount; + uint32_t dwRBitMask; + uint32_t dwGBitMask; + uint32_t dwBBitMask; + uint32_t dwABitMask; + }; + + enum DDS_HEADER_FLAGS + { + DDSD_CAPS = 0x1, + DDSD_HEIGHT = 0x2, + DDSD_WIDTH = 0x4, + DDSD_PITCH = 0x8, + DDSD_PIXELFORMAT = 0x1000, + DDSD_MIPMAPCOUNT = 0x20000, + DDSD_LINEARSIZE = 0x80000, + DDSD_DEPTH = 0x800000, + }; + + enum DDS_HEADER_CAPS + { + DDSCAPS_COMPLEX = 0x8, + DDSCAPS_TEXTURE = 0x1000, + DDSCAPS_MIPMAP = 0x400000, + }; + + enum DDS_HEADER_CAPS2 + { + DDSCAPS2_CUBEMAP = 0x200, + DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, + DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, + DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, + DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, + DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, + DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, + DDSCAPS2_VOLUME = 0x200000, + }; + + struct DDS_HEADER + { + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwHeight; + uint32_t dwWidth; + uint32_t dwPitchOrLinearSize; + uint32_t dwDepth; + uint32_t dwMipMapCount; + uint32_t dwReserved1[11]; + DDS_PIXELFORMAT ddspf; + uint32_t dwCaps; + uint32_t dwCaps2; + uint32_t dwCaps3; + uint32_t dwCaps4; + uint32_t dwReserved2; + }; + + enum DDS_DIMENSION + { + DDS_DIMENSION_TEXTURE1D = 0x2, + DDS_DIMENSION_TEXTURE2D, + DDS_DIMENSION_TEXTURE3D + }; + + enum DDS_RESOURCE_MISC + { + DDS_RESOURCE_MISC_TEXTURECUBE = 0x4 + }; + + struct DDS_HEADER_DXT10 + { + DXGI_FORMAT dxgiFormat; + DDS_DIMENSION resourceDimension; + uint32_t miscFlag; + uint32_t arraySize; + uint32_t miscFlags2; + }; +} diff --git a/src/ObjCommon/Image/ImageFormat.cpp b/src/ObjCommon/Image/ImageFormat.cpp index 6eb311ef..ec010e6d 100644 --- a/src/ObjCommon/Image/ImageFormat.cpp +++ b/src/ObjCommon/Image/ImageFormat.cpp @@ -18,12 +18,28 @@ DXGI_FORMAT ImageFormat::GetDxgiFormat() const } ImageFormatUnsigned::ImageFormatUnsigned(const ImageFormatId id, const DXGI_FORMAT dxgiFormat, - const unsigned bitPerPixel, const unsigned rOffset, const unsigned rSize, + const unsigned bitsPerPixel, const unsigned rOffset, const unsigned rSize, const unsigned gOffset, const unsigned gSize, const unsigned bOffset, const unsigned bSize, const unsigned aOffset, const unsigned aSize) : ImageFormat(id, dxgiFormat) { - m_bit_per_pixel = bitPerPixel; + m_bits_per_pixel = bitsPerPixel; + + m_r_offset = rOffset; + m_r_size = rSize; + m_r_mask = (UINT64_MAX << rOffset) & ~(UINT64_MAX << (rOffset + rSize)); + + m_g_offset = gOffset; + m_g_size = gSize; + m_g_mask = (UINT64_MAX << gOffset) & ~(UINT64_MAX << (gOffset + gSize)); + + m_b_offset = bOffset; + m_b_size = bSize; + m_b_mask = (UINT64_MAX << bOffset) & ~(UINT64_MAX << (bOffset + bSize)); + + m_a_offset = aOffset; + m_a_size = aSize; + m_a_mask = (UINT64_MAX << aOffset) & ~(UINT64_MAX << (aOffset + aSize)); } ImageFormatType ImageFormatUnsigned::GetType() const @@ -45,7 +61,12 @@ size_t ImageFormatUnsigned::GetSizeOfMipLevel(const unsigned mipLevel, const uns if (mipDepth == 0) mipDepth = 1; - return mipWidth * mipHeight * mipDepth * m_bit_per_pixel / 8; + return mipWidth * mipHeight * mipDepth * m_bits_per_pixel / 8; +} + +size_t ImageFormatUnsigned::GetPitch(const unsigned width) const +{ + return (width * m_bits_per_pixel + 7) / 8; } ImageFormatBlockCompressed::ImageFormatBlockCompressed(const ImageFormatId id, const DXGI_FORMAT dxgiFormat, @@ -82,6 +103,11 @@ size_t ImageFormatBlockCompressed::GetSizeOfMipLevel(const unsigned mipLevel, co return blockCount * m_bits_per_block / 8; } +size_t ImageFormatBlockCompressed::GetPitch(const unsigned width) const +{ + return (width + m_block_size - 1) / m_block_size * (m_bits_per_block / 8); +} + const ImageFormatUnsigned ImageFormat::FORMAT_R8_G8_B8(ImageFormatId::R8_G8_B8, DXGI_FORMAT_UNKNOWN, 24, 0, 8, 8, 8, 16, 8, 0, 0); const ImageFormatUnsigned ImageFormat::FORMAT_R8_G8_B8_A8(ImageFormatId::R8_G8_B8_A8, DXGI_FORMAT_R8G8B8A8_UNORM, diff --git a/src/ObjCommon/Image/ImageFormat.h b/src/ObjCommon/Image/ImageFormat.h index 96db6101..11b14a5e 100644 --- a/src/ObjCommon/Image/ImageFormat.h +++ b/src/ObjCommon/Image/ImageFormat.h @@ -1,5 +1,6 @@ #pragma once +#include #include enum class ImageFormatId @@ -42,6 +43,7 @@ public: virtual ImageFormatType GetType() const = 0; virtual size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const = 0; + virtual size_t GetPitch(unsigned width) const = 0; static const ImageFormatUnsigned FORMAT_R8_G8_B8; static const ImageFormatUnsigned FORMAT_R8_G8_B8_A8; @@ -56,25 +58,39 @@ public: class ImageFormatUnsigned final : public ImageFormat { - unsigned m_bit_per_pixel; - public: - ImageFormatUnsigned(ImageFormatId id, DXGI_FORMAT dxgiFormat, unsigned bitPerPixel, unsigned rOffset, + unsigned m_bits_per_pixel; + unsigned m_r_offset; + unsigned m_r_size; + uint64_t m_r_mask; + unsigned m_g_offset; + unsigned m_g_size; + uint64_t m_g_mask; + unsigned m_b_offset; + unsigned m_b_size; + uint64_t m_b_mask; + unsigned m_a_offset; + unsigned m_a_size; + uint64_t m_a_mask; + + ImageFormatUnsigned(ImageFormatId id, DXGI_FORMAT dxgiFormat, unsigned bitsPerPixel, unsigned rOffset, unsigned rSize, unsigned gOffset, unsigned gSize, unsigned bOffset, unsigned bSize, unsigned aOffset, unsigned aSize); ImageFormatType GetType() const override; size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const override; + size_t GetPitch(unsigned width) const override; }; class ImageFormatBlockCompressed final : public ImageFormat { +public: unsigned m_block_size; unsigned m_bits_per_block; -public: ImageFormatBlockCompressed(ImageFormatId id, DXGI_FORMAT dxgiFormat, unsigned blockSize, unsigned bitsPerBlock); ImageFormatType GetType() const override; size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const override; + size_t GetPitch(unsigned width) const override; }; diff --git a/src/ObjCommon/Image/Texture.cpp b/src/ObjCommon/Image/Texture.cpp index 9bc327aa..8835d5e1 100644 --- a/src/ObjCommon/Image/Texture.cpp +++ b/src/ObjCommon/Image/Texture.cpp @@ -50,7 +50,7 @@ void Texture::Allocate() for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { - storageRequirement += GetSizeOfMipLevel(currentMipLevel); + storageRequirement += GetSizeOfMipLevel(currentMipLevel) * GetFaceCount(); } if (storageRequirement > 0) @@ -70,6 +70,11 @@ bool Texture::HasMipMaps() const return m_has_mip_maps; } +uint8_t* Texture::GetBufferForMipLevel(const int mipLevel) +{ + return GetBufferForMipLevel(mipLevel, 0); +} + // ============================================== // ================ Texture2D =================== // ============================================== @@ -106,6 +111,11 @@ Texture2D& Texture2D::operator=(Texture2D&& other) noexcept return *this; } +Texture::Type Texture2D::GetType() const +{ + return Type::TYPE_2D; +} + unsigned Texture2D::GetWidth() const { return m_width; @@ -116,6 +126,16 @@ unsigned Texture2D::GetHeight() const return m_height; } +unsigned Texture2D::GetDepth() const +{ + return 1; +} + +int Texture2D::GetFaceCount() const +{ + return 1; +} + size_t Texture2D::GetSizeOfMipLevel(const int mipLevel) const { return m_format->GetSizeOfMipLevel(mipLevel, m_width, m_height, 1); @@ -136,15 +156,19 @@ int Texture2D::GetMipMapCount() const return mipMapCount; } -uint8_t* Texture2D::GetBufferForMipLevel(const int mipLevel) +uint8_t* Texture2D::GetBufferForMipLevel(const int mipLevel, const int face) { assert(mipLevel >= 0); assert(mipLevel < (m_has_mip_maps ? GetMipMapCount() : 1)); + assert(face == 0); assert(m_data); if (mipLevel < 0 || mipLevel >= (m_has_mip_maps ? GetMipMapCount() : 1)) return nullptr; + if (face != 0) + return nullptr; + if (!m_data) return nullptr; @@ -189,9 +213,40 @@ TextureCube& TextureCube::operator=(TextureCube&& other) noexcept return *this; } -size_t TextureCube::GetSizeOfMipLevel(const int mipLevel) const +Texture::Type TextureCube::GetType() const { - return m_format->GetSizeOfMipLevel(mipLevel, m_width, m_height, 1) * FACE_COUNT; + return Type::TYPE_CUBE; +} + +int TextureCube::GetFaceCount() const +{ + return FACE_COUNT; +} + +uint8_t* TextureCube::GetBufferForMipLevel(const int mipLevel, const int face) +{ + assert(mipLevel >= 0); + assert(mipLevel < (m_has_mip_maps ? GetMipMapCount() : 1)); + assert(face >= 0); + assert(face < FACE_COUNT); + assert(m_data); + + if (mipLevel < 0 || mipLevel >= (m_has_mip_maps ? GetMipMapCount() : 1)) + return nullptr; + + if (face < 0 || face >= FACE_COUNT) + return nullptr; + + if (!m_data) + return nullptr; + + size_t bufferOffset = 0; + for (int previousMipLevel = 0; previousMipLevel < mipLevel; previousMipLevel++) + { + bufferOffset += GetSizeOfMipLevel(previousMipLevel) * FACE_COUNT; + } + + return &m_data[bufferOffset + GetSizeOfMipLevel(mipLevel) * face]; } // ============================================== @@ -235,6 +290,11 @@ Texture3D& Texture3D::operator=(Texture3D&& other) noexcept return *this; } +Texture::Type Texture3D::GetType() const +{ + return Type::TYPE_3D; +} + unsigned Texture3D::GetWidth() const { return m_width; @@ -250,6 +310,11 @@ unsigned Texture3D::GetDepth() const return m_depth; } +int Texture3D::GetFaceCount() const +{ + return 1; +} + size_t Texture3D::GetSizeOfMipLevel(const int mipLevel) const { return m_format->GetSizeOfMipLevel(mipLevel, m_width, m_height, m_depth); @@ -270,15 +335,19 @@ int Texture3D::GetMipMapCount() const return mipMapCount; } -uint8_t* Texture3D::GetBufferForMipLevel(const int mipLevel) +uint8_t* Texture3D::GetBufferForMipLevel(const int mipLevel, const int face) { assert(mipLevel >= 0); assert(mipLevel < (m_has_mip_maps ? GetMipMapCount() : 1)); + assert(face == 0); assert(m_data); if (mipLevel < 0 || mipLevel >= (m_has_mip_maps ? GetMipMapCount() : 1)) return nullptr; + if (face != 0) + return nullptr; + if (!m_data) return nullptr; diff --git a/src/ObjCommon/Image/Texture.h b/src/ObjCommon/Image/Texture.h index f7498c11..fb6727ca 100644 --- a/src/ObjCommon/Image/Texture.h +++ b/src/ObjCommon/Image/Texture.h @@ -4,6 +4,14 @@ class Texture { +public: + enum class Type + { + TYPE_2D, + TYPE_CUBE, + TYPE_3D + }; + protected: const ImageFormat* m_format; bool m_has_mip_maps; @@ -21,12 +29,19 @@ public: Texture& operator=(const Texture& other) = delete; const ImageFormat* GetFormat() const; + virtual Type GetType() const = 0; + + virtual unsigned GetWidth() const = 0; + virtual unsigned GetHeight() const = 0; + virtual unsigned GetDepth() const = 0; + virtual int GetFaceCount() const = 0; void Allocate(); bool Empty() const; virtual size_t GetSizeOfMipLevel(int mipLevel) const = 0; - virtual uint8_t* GetBufferForMipLevel(int mipLevel) = 0; + virtual uint8_t* GetBufferForMipLevel(int mipLevel, int face) = 0; + uint8_t* GetBufferForMipLevel(int mipLevel); bool HasMipMaps() const; virtual int GetMipMapCount() const = 0; @@ -48,11 +63,15 @@ public: Texture2D& operator=(const Texture2D& other) = delete; Texture2D& operator=(Texture2D&& other) noexcept; - unsigned GetWidth() const; - unsigned GetHeight() const; + Type GetType() const override; + + unsigned GetWidth() const override; + unsigned GetHeight() const override; + unsigned GetDepth() const override; + int GetFaceCount() const override; size_t GetSizeOfMipLevel(int mipLevel) const override; - uint8_t* GetBufferForMipLevel(int mipLevel) override; + uint8_t* GetBufferForMipLevel(int mipLevel, int face) override; int GetMipMapCount() const override; }; @@ -71,7 +90,11 @@ public: TextureCube& operator=(const TextureCube& other) = delete; TextureCube& operator=(TextureCube&& other) noexcept; - size_t GetSizeOfMipLevel(int mipLevel) const override; + Type GetType() const override; + + int GetFaceCount() const override; + + uint8_t* GetBufferForMipLevel(int mipLevel, int face) override; }; class Texture3D final : public Texture @@ -90,12 +113,15 @@ public: Texture3D& operator=(const Texture3D& other) = delete; Texture3D& operator=(Texture3D&& other) noexcept; - unsigned GetWidth() const; - unsigned GetHeight() const; - unsigned GetDepth() const; + Type GetType() const override; + + unsigned GetWidth() const override; + unsigned GetHeight() const override; + unsigned GetDepth() const override; + int GetFaceCount() const override; size_t GetSizeOfMipLevel(int mipLevel) const override; - uint8_t* GetBufferForMipLevel(int mipLevel) override; + uint8_t* GetBufferForMipLevel(int mipLevel, int face) override; int GetMipMapCount() const override; }; \ No newline at end of file diff --git a/src/ObjWriting/Image/DdsWriter.cpp b/src/ObjWriting/Image/DdsWriter.cpp index a586dc42..7bae2309 100644 --- a/src/ObjWriting/Image/DdsWriter.cpp +++ b/src/ObjWriting/Image/DdsWriter.cpp @@ -1,11 +1,278 @@ #include "DdsWriter.h" +#include + +#include "Image/DdsTypes.h" + +using namespace dds; + +class DdsWriterInternal +{ + static constexpr uint32_t DDS_MAGIC = MakeFourCc('D', 'D', 'S', ' '); + static constexpr uint32_t DDS_PF_DXT10_EXTENSION = MakeFourCc('D', 'X', '1', '0'); + static constexpr uint32_t DDS_PF_BC1 = MakeFourCc('D', 'X', 'T', '1'); + static constexpr uint32_t DDS_PF_BC2 = MakeFourCc('D', 'X', 'T', '3'); + static constexpr uint32_t DDS_PF_BC3 = MakeFourCc('D', 'X', 'T', '5'); + +public: + enum class ImageFormatSupportStatus + { + UNSUPPORTED, + SUPPORTED_WITH_SIMPLE_HEADER, + SUPPORTED_WITH_DXT10_HEADER + }; + +private: + FileAPI::IFile* m_file; + Texture* m_texture; + ImageFormatSupportStatus m_support_status; + + void PrepareHeader(DDS_HEADER* header) const + { + header->dwSize = sizeof DDS_HEADER; + header->dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; + + if (m_texture->HasMipMaps()) + header->dwFlags |= DDSD_MIPMAPCOUNT; + + if (m_texture->GetFormat()->GetType() == ImageFormatType::BLOCK_COMPRESSED) + header->dwFlags |= DDSD_LINEARSIZE; + else + header->dwFlags |= DDSD_PITCH; + + if (m_texture->GetType() == Texture::Type::TYPE_3D) + header->dwFlags |= DDSD_DEPTH; + + header->dwWidth = m_texture->GetWidth(); + header->dwHeight = m_texture->GetHeight(); + header->dwDepth = m_texture->GetDepth(); + header->dwPitchOrLinearSize = m_texture->GetFormat()->GetPitch(m_texture->GetWidth()); + header->dwMipMapCount = m_texture->HasMipMaps() ? m_texture->GetMipMapCount() : 1; + + memset(header->dwReserved1, 0, sizeof DDS_HEADER::dwReserved1); + + header->dwCaps = DDSCAPS_TEXTURE; + if (m_texture->HasMipMaps()) + header->dwCaps |= DDSCAPS_MIPMAP; + if (m_texture->GetType() == Texture::Type::TYPE_CUBE || m_texture->HasMipMaps()) + header->dwCaps |= DDSCAPS_COMPLEX; + + header->dwCaps2 = 0; + if (m_texture->GetType() == Texture::Type::TYPE_CUBE) + { + header->dwCaps2 |= + DDSCAPS2_CUBEMAP + | DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX + | DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY + | DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ; + } + else if (m_texture->GetType() == Texture::Type::TYPE_3D) + { + header->dwCaps2 |= DDSCAPS2_VOLUME; + } + + header->dwCaps3 = 0; + header->dwCaps4 = 0; + header->dwReserved2 = 0; + } + + void SetPixelFormat(DDS_PIXELFORMAT* pixelFormat) const + { + pixelFormat->dwSize = sizeof DDS_PIXELFORMAT; + pixelFormat->dwFlags = 0; + + const auto* imageFormat = m_texture->GetFormat(); + switch (imageFormat->GetType()) + { + case ImageFormatType::UNSIGNED: + { + const auto* imageFormatUnsigned = dynamic_cast(imageFormat); + + if (imageFormatUnsigned->m_r_size == 0 + && imageFormatUnsigned->m_g_size == 0 + && imageFormatUnsigned->m_b_size == 0 + && imageFormatUnsigned->m_a_size > 0) + { + pixelFormat->dwFlags |= DDPF_ALPHA; + } + else + { + if (imageFormatUnsigned->m_r_size > 0 + && imageFormatUnsigned->m_g_size == 0 + && imageFormatUnsigned->m_b_size == 0) + { + pixelFormat->dwFlags |= DDPF_LUMINANCE; + } + else if (imageFormatUnsigned->m_r_size > 0 + || imageFormatUnsigned->m_g_size > 0 + || imageFormatUnsigned->m_b_size > 0) + { + pixelFormat->dwFlags |= DDPF_RGB; + } + + if (imageFormatUnsigned->m_a_size > 0) + { + pixelFormat->dwFlags |= DDPF_ALPHAPIXELS; + } + } + + pixelFormat->dwRGBBitCount = imageFormatUnsigned->m_bits_per_pixel; + pixelFormat->dwRBitMask = static_cast(imageFormatUnsigned->m_r_mask); + pixelFormat->dwGBitMask = static_cast(imageFormatUnsigned->m_g_mask); + pixelFormat->dwBBitMask = static_cast(imageFormatUnsigned->m_b_mask); + pixelFormat->dwABitMask = static_cast(imageFormatUnsigned->m_a_mask); + } + break; + case ImageFormatType::BLOCK_COMPRESSED: + { + pixelFormat->dwFlags |= DDPF_FOURCC; + pixelFormat->dwRGBBitCount = 0; + pixelFormat->dwRBitMask = 0; + pixelFormat->dwGBitMask = 0; + pixelFormat->dwBBitMask = 0; + pixelFormat->dwABitMask = 0; + + switch (imageFormat->GetId()) + { + case ImageFormatId::BC1: + pixelFormat->dwFourCC = DDS_PF_BC1; + break; + case ImageFormatId::BC2: + pixelFormat->dwFourCC = DDS_PF_BC2; + break; + case ImageFormatId::BC3: + pixelFormat->dwFourCC = DDS_PF_BC3; + break; + default: + assert(false); + break; + } + } + break; + default: + break; + } + } + + static void MarkHeaderAsExtendedByHeaderDxt10(DDS_PIXELFORMAT* pixelFormat) + { + pixelFormat->dwSize = sizeof DDS_PIXELFORMAT; + pixelFormat->dwFlags = DDPF_FOURCC; + pixelFormat->dwFourCC = DDS_PF_DXT10_EXTENSION; + pixelFormat->dwRGBBitCount = 0; + pixelFormat->dwRBitMask = 0; + pixelFormat->dwGBitMask = 0; + pixelFormat->dwBBitMask = 0; + pixelFormat->dwABitMask = 0; + } + + void PrepareHeaderDxt10(DDS_HEADER_DXT10* header) + { + header->dxgiFormat = m_texture->GetFormat()->GetDxgiFormat(); + + switch (m_texture->GetType()) + { + case Texture::Type::TYPE_2D: + case Texture::Type::TYPE_CUBE: + header->resourceDimension = DDS_DIMENSION_TEXTURE2D; + break; + case Texture::Type::TYPE_3D: + header->resourceDimension = DDS_DIMENSION_TEXTURE3D; + break; + } + + header->miscFlag = 0; + if (m_texture->GetType() == Texture::Type::TYPE_CUBE) + header->miscFlag |= DDS_RESOURCE_MISC_TEXTURECUBE; + + header->arraySize = m_texture->GetFaceCount(); + header->miscFlags2 = 0; + } + +public: + static ImageFormatSupportStatus GetSupportStatusForImageFormat(const ImageFormat* format) + { + assert(format != nullptr); + + if (auto* imageFormatUnsigned = dynamic_cast(format)) + { + if (imageFormatUnsigned->m_bits_per_pixel <= sizeof uint32_t * 8) + return ImageFormatSupportStatus::SUPPORTED_WITH_SIMPLE_HEADER; + } + else + { + switch (format->GetId()) + { + case ImageFormatId::BC1: + case ImageFormatId::BC2: + case ImageFormatId::BC3: + return ImageFormatSupportStatus::SUPPORTED_WITH_SIMPLE_HEADER; + default: + break; + } + } + + return format->GetDxgiFormat() != DXGI_FORMAT_UNKNOWN + ? ImageFormatSupportStatus::SUPPORTED_WITH_DXT10_HEADER + : ImageFormatSupportStatus::UNSUPPORTED; + } + + DdsWriterInternal(FileAPI::IFile* file, Texture* texture) + { + m_file = file; + m_texture = texture; + m_support_status = GetSupportStatusForImageFormat(texture->GetFormat()); + } + + bool DumpImage() + { + if (m_file->Write(&DDS_MAGIC, sizeof uint32_t, 1) != 1) + return false; + + DDS_HEADER ddsHeader{}; + PrepareHeader(&ddsHeader); + if (m_support_status == ImageFormatSupportStatus::SUPPORTED_WITH_SIMPLE_HEADER) + { + SetPixelFormat(&ddsHeader.ddspf); + + if (m_file->Write(&ddsHeader, sizeof DDS_HEADER, 1) != 1) + return false; + } + else if (m_support_status == ImageFormatSupportStatus::SUPPORTED_WITH_DXT10_HEADER) + { + DDS_HEADER_DXT10 ddsHeaderDxt10{}; + + MarkHeaderAsExtendedByHeaderDxt10(&ddsHeader.ddspf); + PrepareHeaderDxt10(&ddsHeaderDxt10); + + if (m_file->Write(&ddsHeader, sizeof DDS_HEADER, 1) != 1 + || m_file->Write(&ddsHeaderDxt10, sizeof DDS_HEADER_DXT10, 1) != 1) + return false; + } + + const int mipCount = m_texture->HasMipMaps() ? m_texture->GetMipMapCount() : 1; + for (int face = 0; face < m_texture->GetFaceCount(); face++) + { + for (int mipLevel = 0; mipLevel < mipCount; mipLevel++) + { + const size_t sizeOfMipLevel = m_texture->GetSizeOfMipLevel(mipLevel); + + if (m_file->Write(m_texture->GetBufferForMipLevel(mipLevel, face), 1, sizeOfMipLevel) != sizeOfMipLevel) + return false; + } + } + + return true; + } +}; + DdsWriter::~DdsWriter() = default; bool DdsWriter::SupportsImageFormat(const ImageFormat* imageFormat) { - return true; + return DdsWriterInternal::GetSupportStatusForImageFormat(imageFormat) + != DdsWriterInternal::ImageFormatSupportStatus::UNSUPPORTED; } std::string DdsWriter::GetFileExtension() @@ -15,4 +282,6 @@ std::string DdsWriter::GetFileExtension() void DdsWriter::DumpImage(FileAPI::IFile* file, Texture* texture) { + DdsWriterInternal writer(file, texture); + writer.DumpImage(); } diff --git a/src/ObjWriting/Image/DdsWriter.h b/src/ObjWriting/Image/DdsWriter.h index ad6dddff..6b7003c3 100644 --- a/src/ObjWriting/Image/DdsWriter.h +++ b/src/ObjWriting/Image/DdsWriter.h @@ -8,5 +8,5 @@ public: bool SupportsImageFormat(const ImageFormat * imageFormat) override; std::string GetFileExtension() override; - void DumpImage(FileAPI::IFile * file, Texture * texture) override; + void DumpImage(FileAPI::IFile* file, Texture* texture) override; };