Add Image loading for dds textures for iw3

This commit is contained in:
Jan 2021-04-28 15:29:56 +02:00
parent 8ca0eef429
commit 775bf01752
7 changed files with 458 additions and 2 deletions

View File

@ -787,6 +787,19 @@ namespace IW3
TS_WATER_MAP = 0xB,
};
enum ImageCategory
{
IMG_CATEGORY_UNKNOWN = 0x0,
IMG_CATEGORY_AUTO_GENERATED = 0x1,
IMG_CATEGORY_LIGHTMAP = 0x2,
IMG_CATEGORY_LOAD_FROM_FILE = 0x3,
IMG_CATEGORY_RAW = 0x4,
IMG_CATEGORY_FIRST_UNMANAGED = 0x5,
IMG_CATEGORY_WATER = 0x5,
IMG_CATEGORY_RENDERTARGET = 0x6,
IMG_CATEGORY_TEMP = 0x7,
};
struct GfxImage
{
MapType mapType;

View File

@ -0,0 +1,132 @@
#include "AssetLoaderGfxImage.h"
#include <cstring>
#include <iostream>
#include "Game/IW3/IW3.h"
#include "Image/DdsLoader.h"
#include "Pool/GlobalAssetPool.h"
#include "Image/IwiTypes.h"
using namespace IW3;
void* AssetLoaderGfxImage::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory)
{
auto* image = memory->Create<GfxImage>();
memset(image, 0, sizeof(GfxImage));
image->name = memory->Dup(assetName.c_str());
return image;
}
bool AssetLoaderGfxImage::CanLoadFromRaw() const
{
return true;
}
bool AssetLoaderGfxImage::LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const
{
// Do not load any GfxImages from raw for now that are not loaded
// TODO: Load iwis and add streaming info to asset
if (assetName.empty() || assetName[0] != '*')
return false;
std::string safeAssetName = assetName;
for (auto& c : safeAssetName)
{
switch (c)
{
case '*':
c = '_';
break;
default:
break;
}
}
const auto file = searchPath->Open("images/" + safeAssetName + ".dds");
if (!file.IsOpen())
return false;
const DdsLoader ddsLoader(zone->GetMemory());
auto* texture = ddsLoader.LoadDds(*file.m_stream);
if (texture == nullptr)
{
std::cout << "Failed to load dds file for image asset \"" << assetName << "\"" << std::endl;
return false;
}
auto* image = memory->Create<GfxImage>();
image->name = memory->Dup(assetName.c_str());
image->picmip.platform[0] = 0;
image->picmip.platform[1] = 0;
image->noPicmip = !texture->HasMipMaps();
image->semantic = TS_FUNCTION;
image->track = 0;
image->cardMemory.platform[0] = 0;
image->cardMemory.platform[1] = 0;
image->width = static_cast<uint16_t>(texture->GetWidth());
image->height = static_cast<uint16_t>(texture->GetHeight());
image->depth = static_cast<uint16_t>(texture->GetDepth());
image->category = IMG_CATEGORY_AUTO_GENERATED;
image->delayLoadPixels = false;
switch (texture->GetTextureType())
{
case TextureType::T_2D:
image->mapType = MAPTYPE_2D;
break;
case TextureType::T_3D:
image->mapType = MAPTYPE_3D;
break;
case TextureType::T_CUBE:
image->mapType = MAPTYPE_CUBE;
break;
default:
image->mapType = MAPTYPE_NONE;
break;
}
const auto mipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1;
const auto faceCount = texture->GetFaceCount();
size_t dataSize = 0;
for (auto mipLevel = 0u; mipLevel < mipCount; mipLevel++)
dataSize += texture->GetSizeOfMipLevel(mipLevel) * faceCount;
auto* loadDef = static_cast<GfxImageLoadDef*>(zone->GetMemory()->Alloc(offsetof(GfxImageLoadDef, data) + dataSize));
image->texture.loadDef = loadDef;
loadDef->levelCount = static_cast<char>(mipCount);
loadDef->flags = 0;
if (!texture->HasMipMaps())
loadDef->flags |= iwi6::IMG_FLAG_NOMIPMAPS;
if (texture->GetTextureType() == TextureType::T_CUBE)
loadDef->flags |= iwi6::IMG_FLAG_CUBEMAP;
if (texture->GetTextureType() == TextureType::T_3D)
loadDef->flags |= iwi6::IMG_FLAG_VOLMAP;
loadDef->dimensions[0] = image->width;
loadDef->dimensions[1] = image->height;
loadDef->dimensions[2] = image->depth;
loadDef->format = static_cast<int>(texture->GetFormat()->GetD3DFormat());
loadDef->resourceSize = dataSize;
char* currentDataBuffer = loadDef->data;
for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++)
{
const auto mipSize = texture->GetSizeOfMipLevel(mipLevel);
for (auto face = 0; face < faceCount; face++)
{
memcpy(currentDataBuffer, texture->GetBufferForMipLevel(mipLevel, face), mipSize);
currentDataBuffer += mipSize;
}
}
manager->AddAsset(ASSET_TYPE_IMAGE, assetName, image);
return true;
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "Game/IW3/IW3.h"
#include "AssetLoading/BasicAssetLoader.h"
#include "AssetLoading/IAssetLoadingManager.h"
#include "SearchPath/ISearchPath.h"
namespace IW3
{
class AssetLoaderGfxImage final : public BasicAssetLoader<ASSET_TYPE_IMAGE, GfxImage>
{
public:
_NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override;
_NODISCARD bool CanLoadFromRaw() const override;
bool LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override;
};
}

View File

@ -4,6 +4,7 @@
#include "Game/IW3/GameAssetPoolIW3.h"
#include "ObjContainer/IPak/IPak.h"
#include "ObjLoading.h"
#include "AssetLoaders/AssetLoaderGfxImage.h"
#include "AssetLoaders/AssetLoaderRawFile.h"
#include "AssetLoading/AssetLoadingManager.h"
#include "Image/Dx9TextureLoader.h"
@ -23,7 +24,7 @@ ObjLoader::ObjLoader()
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_XMODEL, XModel))
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_MATERIAL, Material))
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_TECHNIQUE_SET, MaterialTechniqueSet))
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_IMAGE, GfxImage))
REGISTER_ASSET_LOADER(AssetLoaderGfxImage)
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_SOUND, snd_alias_list_t))
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_SOUND_CURVE, SndCurve))
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_LOADED_SOUND, LoadedSound))

View File

@ -0,0 +1,280 @@
#include "DdsLoader.h"
#include <iostream>
#include "Utils/ClassUtils.h"
#include "Utils/FileUtils.h"
#include "Image/DdsTypes.h"
class DdsLoaderInternal
{
static constexpr auto DDS_MAGIC = FileUtils::MakeMagic32('D', 'D', 'S', ' ');
MemoryManager* m_memory_manager;
std::istream& m_stream;
TextureType m_texture_type;
bool m_has_mip_maps;
size_t m_width;
size_t m_height;
size_t m_depth;
const ImageFormat* m_format;
_NODISCARD bool ReadMagic() const
{
uint32_t magic;
m_stream.read(reinterpret_cast<char*>(&magic), sizeof(magic));
if (m_stream.gcount() != sizeof(magic))
{
std::cout << "Failed to read dds data" << std::endl;
return false;
}
if (magic != DDS_MAGIC)
{
std::cout << "Invalid magic for dds" << std::endl;
return false;
}
return true;
}
_NODISCARD bool ReadDxt10Header()
{
DDS_HEADER_DXT10 headerDx10{};
m_stream.read(reinterpret_cast<char*>(&headerDx10), sizeof(headerDx10));
if (m_stream.gcount() != sizeof(headerDx10))
{
std::cout << "Failed to read dds data" << std::endl;
return false;
}
if (headerDx10.resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE3D)
{
m_texture_type = TextureType::T_3D;
}
else if (headerDx10.resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE2D)
{
if (headerDx10.miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE || headerDx10.arraySize == 6)
{
m_texture_type = TextureType::T_CUBE;
}
else
{
m_texture_type = TextureType::T_2D;
}
}
else
{
std::cout << "Unsupported dds resourceDimension " << headerDx10.resourceDimension << std::endl;
return false;
}
for (const auto* imageFormat : ImageFormat::ALL_FORMATS)
{
if (imageFormat->GetDxgiFormat() == headerDx10.dxgiFormat)
{
m_format = imageFormat;
return true;
}
}
std::cout << "Unsupported dds dxgi format " << headerDx10.dxgiFormat << std::endl;
return false;
}
_NODISCARD bool ReadPixelFormatFourCc(DDS_PIXELFORMAT& pf)
{
switch (pf.dwFourCC)
{
case FileUtils::MakeMagic32('D', 'X', 'T', '1'):
m_format = &ImageFormat::FORMAT_BC1;
return true;
case FileUtils::MakeMagic32('D', 'X', 'T', '3'):
m_format = &ImageFormat::FORMAT_BC2;
return true;
case FileUtils::MakeMagic32('D', 'X', 'T', '5'):
m_format = &ImageFormat::FORMAT_BC3;
return true;
case FileUtils::MakeMagic32('D', 'X', '1', '0'):
return ReadDxt10Header();
default:
std::cout << "Unknown dds FourCC " << pf.dwFourCC << std::endl;
return false;
}
}
static void ExtractSizeAndOffsetFromMask(uint32_t mask, unsigned& offset, unsigned& size)
{
offset = 0;
size = 0;
if (mask == 0)
return;
while ((mask & 1) == 0)
{
offset++;
mask >>= 1;
}
while ((mask & 1) == 1)
{
size++;
mask >>= 1;
}
}
_NODISCARD bool ReadPixelFormatUnsigned(DDS_PIXELFORMAT& pf)
{
unsigned rOffset, rSize, gOffset, gSize, bOffset, bSize, aOffset, aSize;
ExtractSizeAndOffsetFromMask(pf.dwRBitMask, rOffset, rSize);
ExtractSizeAndOffsetFromMask(pf.dwGBitMask, gOffset, gSize);
ExtractSizeAndOffsetFromMask(pf.dwBBitMask, bOffset, bSize);
ExtractSizeAndOffsetFromMask(pf.dwABitMask, aOffset, aSize);
for (const auto* imageFormat : ImageFormat::ALL_FORMATS)
{
if (imageFormat->GetType() != ImageFormatType::UNSIGNED)
continue;
const auto* unsignedImageFormat = dynamic_cast<const ImageFormatUnsigned*>(imageFormat);
if (unsignedImageFormat->m_r_offset == rOffset && unsignedImageFormat->m_r_size == rSize
&& unsignedImageFormat->m_g_offset == gOffset && unsignedImageFormat->m_g_size == gSize
&& unsignedImageFormat->m_b_offset == bOffset && unsignedImageFormat->m_b_size == bSize
&& unsignedImageFormat->m_a_offset == aOffset && unsignedImageFormat->m_a_size == aSize)
{
m_format = imageFormat;
return true;
}
}
std::cout << "Failed to find dds pixel format: R=" << std::hex << pf.dwRBitMask
<< " G=" << std::hex << pf.dwGBitMask
<< " B=" << std::hex << pf.dwBBitMask
<< " A=" << std::hex << pf.dwABitMask << std::endl;
return false;
}
_NODISCARD bool ReadPixelFormat(DDS_PIXELFORMAT& pf)
{
if (pf.dwFlags & DDPF_FOURCC)
return ReadPixelFormatFourCc(pf);
return ReadPixelFormatUnsigned(pf);
}
_NODISCARD bool ReadHeader()
{
DDS_HEADER header{};
m_stream.read(reinterpret_cast<char*>(&header), sizeof(header));
if (m_stream.gcount() != sizeof(header))
{
std::cout << "Failed to read dds data" << std::endl;
return false;
}
m_width = header.dwWidth;
m_height = header.dwHeight;
m_depth = header.dwDepth;
m_has_mip_maps = (header.dwCaps & DDSCAPS_MIPMAP) != 0 || header.dwMipMapCount > 1;
if (header.dwCaps2 & DDSCAPS2_CUBEMAP)
m_texture_type = TextureType::T_CUBE;
else if (header.dwDepth > 1)
m_texture_type = TextureType::T_3D;
else
m_texture_type = TextureType::T_2D;
return ReadPixelFormat(header.ddspf);
}
_NODISCARD Texture* ReadTextureData() const
{
Texture* result;
switch (m_texture_type)
{
case TextureType::T_2D:
result = new Texture2D(m_format, m_width, m_height, m_has_mip_maps);
break;
case TextureType::T_3D:
result = new Texture3D(m_format, m_width, m_height, m_depth, m_has_mip_maps);
break;
case TextureType::T_CUBE:
result = new TextureCube(m_format, m_width, m_height, m_has_mip_maps);
break;
default:
return nullptr;
}
const auto mipMapCount = m_has_mip_maps ? result->GetMipMapCount() : 1;
const auto faceCount = m_texture_type == TextureType::T_CUBE ? 6 : 1;
result->Allocate();
for (auto mipLevel = 0; mipLevel < mipMapCount; mipLevel++)
{
const auto mipSize = result->GetSizeOfMipLevel(mipLevel);
for (auto face = 0; face < faceCount; face++)
{
m_stream.read(reinterpret_cast<char*>(result->GetBufferForMipLevel(mipLevel, face)), mipSize);
if (m_stream.gcount() != mipSize)
{
std::cout << "Failed to read texture data from dds" << std::endl;
delete result;
return nullptr;
}
}
}
return result;
}
public:
DdsLoaderInternal(MemoryManager* memoryManager, std::istream& stream)
: m_memory_manager(memoryManager),
m_stream(stream),
m_texture_type(TextureType::T_2D),
m_has_mip_maps(false),
m_width(0u),
m_height(0u),
m_depth(0u),
m_format(nullptr)
{
}
Texture* LoadDds()
{
if (!ReadMagic()
|| !ReadHeader())
{
return nullptr;
}
return ReadTextureData();
}
};
DdsLoader::DdsLoader(MemoryManager* memoryManager)
: m_memory_manager(memoryManager)
{
}
Texture* DdsLoader::LoadDds(std::istream& stream) const
{
DdsLoaderInternal internal(m_memory_manager, stream);
return internal.LoadDds();
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <istream>
#include "Utils/MemoryManager.h"
#include "Image/Texture.h"
class DdsLoader
{
MemoryManager* m_memory_manager;
public:
explicit DdsLoader(MemoryManager* memoryManager);
Texture* LoadDds(std::istream& stream) const;
};

View File

@ -4,7 +4,6 @@
#include <map>
#include <memory>
#include "Image/DdsTypes.h"
#include "Image/TextureConverter.h"