diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.cpp new file mode 100644 index 00000000..c16f2b88 --- /dev/null +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.cpp @@ -0,0 +1,20 @@ +#include "AssetDumperGfxImage.h" +#include "Image/IwiWriter27.h" + +using namespace T6; + +bool AssetDumperGfxImage::ShouldDump(GfxImage* asset) +{ + return asset->loadedSize > 0; +} + +std::string AssetDumperGfxImage::GetFileNameForAsset(Zone* zone, GfxImage* asset) +{ + return "images/" + std::string(asset->name) + ".iwi"; +} + +void AssetDumperGfxImage::DumpAsset(Zone* zone, GfxImage* asset, FileAPI::File* out) +{ + IwiWriter27 writer; + writer.DumpImage(out, asset->texture.texture); +} \ No newline at end of file diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.h new file mode 100644 index 00000000..0944dfe7 --- /dev/null +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +class AssetDumperGfxImage final : public AbstractAssetDumper +{ +protected: + bool ShouldDump(T6::GfxImage* asset) override; + std::string GetFileNameForAsset(Zone* zone, T6::GfxImage* asset) override; + void DumpAsset(Zone* zone, T6::GfxImage* asset, FileAPI::File* out) override; +}; \ No newline at end of file diff --git a/src/ObjWriting/Game/T6/ZoneDumperT6.cpp b/src/ObjWriting/Game/T6/ZoneDumperT6.cpp index 82db9b72..164c52f5 100644 --- a/src/ObjWriting/Game/T6/ZoneDumperT6.cpp +++ b/src/ObjWriting/Game/T6/ZoneDumperT6.cpp @@ -8,6 +8,7 @@ #include "AssetDumpers/AssetDumperScriptParseTree.h" #include "AssetDumpers/AssetDumperStringTable.h" #include "AssetDumpers/AssetDumperLocalizeEntry.h" +#include "AssetDumpers/AssetDumperGfxImage.h" bool ZoneDumperT6::CanHandleZone(Zone* zone) { @@ -32,7 +33,7 @@ bool ZoneDumperT6::DumpZone(Zone* zone, const std::string& basePath) // DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel); // DUMP_ASSET_POOL(AssetDumperMaterial, m_material); // DUMP_ASSET_POOL(AssetDumperTechniqueSet, m_technique_set); - // DUMP_ASSET_POOL(AssetDumperGfxImage, m_image); + DUMP_ASSET_POOL(AssetDumperGfxImage, m_image); // DUMP_ASSET_POOL(AssetDumperSndBank, m_sound_bank); // DUMP_ASSET_POOL(AssetDumperSndPatch, m_sound_patch); // DUMP_ASSET_POOL(AssetDumperClipMap, m_clip_map); diff --git a/src/ObjWriting/Image/IImageWriter.h b/src/ObjWriting/Image/IImageWriter.h new file mode 100644 index 00000000..60f1b773 --- /dev/null +++ b/src/ObjWriting/Image/IImageWriter.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Image/Texture.h" +#include "Utils/FileAPI.h" +#include + +class IImageWriter +{ +public: + virtual ~IImageWriter() = default; + + virtual bool SupportsImageFormat(const ImageFormat* imageFormat) = 0; + virtual std::string GetFileExtension() = 0; + virtual void DumpImage(FileAPI::IFile* file, Texture* texture) = 0; +}; \ No newline at end of file diff --git a/src/ObjWriting/Image/IwiWriter27.cpp b/src/ObjWriting/Image/IwiWriter27.cpp new file mode 100644 index 00000000..0face51c --- /dev/null +++ b/src/ObjWriting/Image/IwiWriter27.cpp @@ -0,0 +1,138 @@ +#include "IwiWriter27.h" +#include + +IwiWriter27::IwiWriter27() += default; + +IwiWriter27::~IwiWriter27() += default; + +iwi27::IwiFormat IwiWriter27::GetIwiFormatForImageFormat(const ImageFormat* imageFormat) +{ + switch (imageFormat->GetId()) + { + case ImageFormatId::R8_G8_B8: + return iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGB; + + case ImageFormatId::R8_G8_B8_A8: + return iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGBA; + + case ImageFormatId::A8: + return iwi27::IwiFormat::IMG_FORMAT_BITMAP_ALPHA; + + case ImageFormatId::R16_G16_B16_A16_FLOAT: + return iwi27::IwiFormat::IMG_FORMAT_A16B16G16R16F; + + case ImageFormatId::BC1: + return iwi27::IwiFormat::IMG_FORMAT_DXT1; + + case ImageFormatId::BC2: + return iwi27::IwiFormat::IMG_FORMAT_DXT3; + + case ImageFormatId::BC3: + return iwi27::IwiFormat::IMG_FORMAT_DXT5; + + case ImageFormatId::BC5: + return iwi27::IwiFormat::IMG_FORMAT_DXN; + + default: + return iwi27::IwiFormat::IMG_FORMAT_INVALID; + } +} + +bool IwiWriter27::SupportsImageFormat(const ImageFormat* imageFormat) +{ + return GetIwiFormatForImageFormat(imageFormat) != iwi27::IwiFormat::IMG_FORMAT_INVALID; +} + +std::string IwiWriter27::GetFileExtension() +{ + return ".iwi"; +} + +void IwiWriter27::WriteVersion(FileAPI::IFile* file) +{ + IwiVersion version{}; + version.tag[0] = 'I'; + version.tag[1] = 'W'; + version.tag[2] = 'i'; + version.version = 27; + + file->Write(&version, sizeof IwiVersion, 1); +} + +void IwiWriter27::FillHeader2D(iwi27::IwiHeader* header, Texture2D* texture) +{ + header->dimensions[0] = texture->GetWidth(); + header->dimensions[1] = texture->GetHeight(); + header->dimensions[2] = 1; +} + +void IwiWriter27::FillHeaderCube(iwi27::IwiHeader* header, TextureCube* texture) +{ + header->dimensions[0] = texture->GetWidth(); + header->dimensions[1] = texture->GetHeight(); + header->dimensions[2] = 1; + header->flags |= iwi27::IwiFlags::IMG_FLAG_CUBEMAP; +} + +void IwiWriter27::FillHeader3D(iwi27::IwiHeader* header, Texture3D* texture) +{ + header->dimensions[0] = texture->GetWidth(); + header->dimensions[1] = texture->GetHeight(); + header->dimensions[2] = texture->GetDepth(); + header->flags |= iwi27::IwiFlags::IMG_FLAG_VOLMAP; +} + +void IwiWriter27::DumpImage(FileAPI::IFile* file, Texture* texture) +{ + assert(file != nullptr); + assert(texture != nullptr); + + WriteVersion(file); + + iwi27::IwiHeader header{}; + header.flags = 0; + header.gamma = 0.0f; + + header.format = static_cast(GetIwiFormatForImageFormat(texture->GetFormat())); + + if (!texture->HasMipMaps()) + header.flags |= iwi27::IwiFlags::IMG_FLAG_NOMIPMAPS; + + for (signed char& i : header.maxGlossForMip) + i = 0; + + size_t currentFileSize = sizeof IwiVersion + sizeof iwi27::IwiHeader; + + const int textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1; + for (int currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--) + { + const size_t mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel); + currentFileSize += mipLevelSize; + + if(currentMipLevel < static_cast(_countof(iwi27::IwiHeader::fileSizeForPicmip))) + header.fileSizeForPicmip[currentMipLevel] = currentFileSize; + } + + if(auto* texture2D = dynamic_cast(texture)) + { + FillHeader2D(&header, texture2D); + } + else if(auto* textureCube = dynamic_cast(texture)) + { + FillHeaderCube(&header, textureCube); + } + else if(auto* texture3D = dynamic_cast(texture)) + { + FillHeader3D(&header, texture3D); + } + + file->Write(&header, sizeof iwi27::IwiHeader, 1); + + for (int currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--) + { + const size_t mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel); + file->Write(texture->GetBufferForMipLevel(currentMipLevel), 1, mipLevelSize); + } +} diff --git a/src/ObjWriting/Image/IwiWriter27.h b/src/ObjWriting/Image/IwiWriter27.h new file mode 100644 index 00000000..b5f8e57e --- /dev/null +++ b/src/ObjWriting/Image/IwiWriter27.h @@ -0,0 +1,27 @@ +#pragma once + +#include "IImageWriter.h" +#include "Image/IwiTypes.h" + +class IwiWriter27 final : public IImageWriter +{ + static iwi27::IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat); + + static void WriteVersion(FileAPI::IFile* file); + static void FillHeader2D(iwi27::IwiHeader* header, Texture2D* texture); + static void FillHeaderCube(iwi27::IwiHeader* header, TextureCube* texture); + static void FillHeader3D(iwi27::IwiHeader* header, Texture3D* texture); + +public: + IwiWriter27(); + IwiWriter27(const IwiWriter27& other) = delete; + IwiWriter27(IwiWriter27&& other) noexcept = delete; + ~IwiWriter27() override; + + IwiWriter27& operator=(const IwiWriter27& other) = delete; + IwiWriter27& operator=(IwiWriter27&& other) noexcept = delete; + + bool SupportsImageFormat(const ImageFormat* imageFormat) override; + std::string GetFileExtension() override; + void DumpImage(FileAPI::IFile* file, Texture* texture) override; +};