verus/Verus/src/CGI/BaseTexture.cpp
2019-10-02 20:36:08 +03:00

350 lines
8 KiB
C++

#include "verus.h"
using namespace verus;
using namespace verus::CGI;
// BaseTexture:
BaseTexture::BaseTexture()
{
}
BaseTexture::~BaseTexture()
{
IO::Async::Cancel(this);
}
bool BaseTexture::IsSRGB() const
{
return
!Str::EndsWith(_C(_name), ".NM.dds") &&
!Str::EndsWith(_C(_name), ".FX.dds") &&
!Str::EndsWith(_C(_name), ".H.dds");
}
void BaseTexture::LoadDDS(CSZ url, int texturePart)
{
_name = url;
IO::Async::I().Load(url, this, IO::Async::TaskDesc(false, false, texturePart));
}
void BaseTexture::LoadDDS(RcBlob blob)
{
IO::DDSHeader header;
memcpy(&header, blob._p, sizeof(header));
if (!header.Validate())
throw VERUS_RUNTIME_ERROR << "Invalid DDS header: " << _name;
_part = header.reserved2;
VERUS_QREF_CONST_SETTINGS;
if (!header.mipmapCount)
header.mipmapCount = 1;
const int maxLod = Math::Max<int>(header.mipmapCount - 4, 0); // Keep 1x1, 2x2, 4x4.
const int lod = Math::Min(settings._gpuTextureLodLevel, maxLod);
if (header.IsBC())
{
auto SelectFormat = [this](Format unorm, Format srgb)
{
return IsSRGB() ? srgb : unorm;
};
TextureDesc desc;
if (header.IsBC1())
desc._format = SelectFormat(Format::unormBC1, Format::srgbBC1);
else if (header.IsBC2())
desc._format = SelectFormat(Format::unormBC2, Format::srgbBC2);
else if (header.IsBC3())
desc._format = SelectFormat(Format::unormBC3, Format::srgbBC3);
desc._width = header.width >> lod;
desc._height = header.height >> lod;
desc._mipLevels = header.mipmapCount - lod;
Init(desc);
int offset = sizeof(header);
for (UINT32 i = 0; i < header.mipmapCount; ++i)
{
UINT32 w = header.width >> i, h = header.height >> i;
if (!w) w = 1;
if (!h) h = 1;
const int levelSize = IO::DDSHeader::ComputeBcLevelSize(w, h, header.IsBC1());
VERUS_RT_ASSERT(offset + levelSize <= blob._size);
if (static_cast<int>(i) >= lod)
UpdateImage(i - lod, blob._p + offset);
offset += levelSize;
}
}
else // Not DXTn?
{
if (header.pixelFormat.flags == IO::DDSHeader::PixelFormatFlags::alpha &&
header.pixelFormat.rgbBitCount == 8)
{
TextureDesc desc;
desc._format = Format::unormR8;
desc._width = header.width;
desc._height = header.height;
desc._mipLevels = header.mipmapCount;
Init(desc);
int offset = sizeof(header);
for (UINT32 i = 0; i < header.mipmapCount; ++i)
{
UINT32 w = header.width >> i, h = header.height >> i;
if (!w) w = 1;
if (!h) h = 1;
UpdateImage(i, blob._p + offset);
offset += w * h;
}
}
else if (header.pixelFormat.flags == IO::DDSHeader::PixelFormatFlags::rgb &&
header.pixelFormat.rgbBitCount == 24)
{
TextureDesc desc;
desc._format = IsSRGB() ? Format::srgbR8G8B8A8 : Format::unormR8G8B8A8;
desc._width = header.width;
desc._height = header.height;
desc._mipLevels = header.mipmapCount;
Init(desc);
int offset = sizeof(header);
for (UINT32 i = 0; i < header.mipmapCount; ++i)
{
int w = header.width >> i, h = header.height >> i;
if (!w) w = 1;
if (!h) h = 1;
const BYTE* p = blob._p + offset;
Vector<UINT32> vData;
vData.resize(w*h);
VERUS_FOR(y, h)
{
VERUS_FOR(x, w)
{
const BYTE color[4] = { p[2], p[1], p[0], 255 };
Utils::CopyColor(vData[y*w + x], color);
p += 3;
}
}
UpdateImage(i, vData.data());
offset += w * h * 3;
}
}
else if (header.pixelFormat.flags == (IO::DDSHeader::PixelFormatFlags::rgb | IO::DDSHeader::PixelFormatFlags::alphaPixels) &&
header.pixelFormat.rgbBitCount == 32)
{
TextureDesc desc;
desc._format = IsSRGB() ? Format::srgbR8G8B8A8 : Format::unormR8G8B8A8;
desc._width = header.width;
desc._height = header.height;
desc._mipLevels = header.mipmapCount;
Init(desc);
int offset = sizeof(header);
for (UINT32 i = 0; i < header.mipmapCount; ++i)
{
int w = header.width >> i, h = header.height >> i;
if (!w) w = 1;
if (!h) h = 1;
const BYTE* p = blob._p + offset;
Vector<UINT32> vData;
vData.resize(w*h);
VERUS_FOR(y, h)
{
VERUS_FOR(x, w)
{
const BYTE color[4] = { p[2], p[1], p[0], p[3] };
Utils::CopyColor(vData[y*w + x], color);
p += 4;
}
}
UpdateImage(i, vData.data());
offset += w * h * 4;
}
}
}
}
void BaseTexture::LoadDDSArray(CSZ* urls)
{
TextureDesc desc;
desc._arrayLayers = 0;
CSZ* urlsCount = urls;
while (*urlsCount)
{
desc._arrayLayers++;
urlsCount++;
}
bool init = true;
int arrayLayer = 0;
while (*urls)
{
_name = *urls;
Vector<BYTE> vData;
IO::FileSystem::LoadResource(*urls, vData);
const BYTE* pData = vData.data();
const size_t size = vData.size();
IO::DDSHeader header;
memcpy(&header, pData, sizeof(header));
if (!header.Validate())
throw VERUS_RUNTIME_ERROR << "Invalid DDS header: " << _name;
VERUS_QREF_CONST_SETTINGS;
if (!header.mipmapCount)
header.mipmapCount = 1;
const int maxLod = Math::Max<int>(header.mipmapCount - 4, 0); // Keep 1x1, 2x2, 4x4.
const int lod = Math::Min(settings._gpuTextureLodLevel, maxLod);
if (header.IsBC())
{
auto SelectFormat = [this](Format unorm, Format srgb)
{
return IsSRGB() ? srgb : unorm;
};
const int w = header.width >> lod;
const int h = header.height >> lod;
if (desc._width)
{
VERUS_RT_ASSERT(desc._width == w);
}
if (desc._height)
{
VERUS_RT_ASSERT(desc._height == h);
}
if (header.IsBC1())
desc._format = SelectFormat(Format::unormBC1, Format::srgbBC1);
else if (header.IsBC2())
desc._format = SelectFormat(Format::unormBC2, Format::srgbBC2);
else if (header.IsBC3())
desc._format = SelectFormat(Format::unormBC3, Format::srgbBC3);
desc._width = w;
desc._height = h;
desc._mipLevels = header.mipmapCount - lod;
if (init)
{
init = false;
Init(desc);
}
int offset = sizeof(header);
VERUS_U_FOR(i, header.mipmapCount)
{
UINT32 w = header.width >> i;
UINT32 h = header.height >> i;
if (!w) w = 1;
if (!h) h = 1;
int levelSize = IO::DDSHeader::ComputeBcLevelSize(w, h, header.IsBC1());
VERUS_RT_ASSERT(offset + levelSize <= size);
if (static_cast<int>(i) >= lod)
UpdateImage(i - lod, pData + offset, arrayLayer);
offset += levelSize;
}
}
urls++;
arrayLayer++;
}
}
void BaseTexture::Async_Run(CSZ url, RcBlob blob)
{
LoadDDS(blob);
}
int BaseTexture::FormatToBytesPerPixel(Format format)
{
switch (format)
{
case Format::unormB4G4R4A4: return 2;
case Format::unormB5G6R5: return 2;
case Format::unormR10G10B10A2: return 4;
case Format::sintR16: return 2;
case Format::unormR8: return 1;
case Format::unormR8G8: return 2;
case Format::unormR8G8B8A8: return 4;
case Format::unormB8G8R8A8: return 4;
case Format::srgbR8G8B8A8: return 4;
case Format::srgbB8G8R8A8: return 4;
case Format::floatR16: return 2;
case Format::floatR16G16: return 4;
case Format::floatR16G16B16A16: return 8;
case Format::floatR32: return 4;
case Format::floatR32G32: return 8;
case Format::floatR32G32B32A32: return 16;
case Format::unormD16: return 2;
case Format::unormD24uintS8: return 4;
case Format::floatD32: return 4;
}
return 0;
}
bool BaseTexture::IsBC(Format format)
{
return format >= Format::unormBC1 && format <= Format::srgbBC3;
}
bool BaseTexture::IsBC1(Format format)
{
return format == Format::unormBC1 || format == Format::srgbBC1;
}
bool BaseTexture::IsDepthFormat(Format format)
{
return format >= Format::unormD16 && format <= Format::floatD32;
}
// TexturePtr:
void TexturePtr::Init(RcTextureDesc desc)
{
VERUS_QREF_RENDERER;
VERUS_RT_ASSERT(!_p);
_p = renderer->InsertTexture();
if (desc._url)
_p->LoadDDS(desc._url, desc._texturePart);
else if (desc._urls)
_p->LoadDDSArray(desc._urls);
else
_p->Init(desc);
}
void TexturePwn::Done()
{
if (_p)
{
VERUS_QREF_RENDERER;
renderer->DeleteTexture(_p);
_p = nullptr;
}
}
TexturePtr TexturePtr::From(PBaseTexture p)
{
TexturePtr ptr;
ptr._p = p;
return ptr;
}