1
0
forked from 0ad/0ad

Support 8bpp textures

When textures.xml specifies format="alpha", the input is expected to be
an
8-bit greyscale PNG, and the output will be an 8-bit uncompressed
alpha-only DDS.

Add format override to CTextureProperties, to select between e.g.
GL_ALPHA
and GL_LUMINANCE for 8-bit textures.

This is needed so fonts can use the new texture system.

This was SVN commit r14015.
This commit is contained in:
Ykkrosh 2013-10-18 15:36:31 +00:00
parent 6a2fac7a58
commit 8799bd98b0
5 changed files with 92 additions and 21 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2013 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -65,6 +65,7 @@ struct CTextureConverter::ConversionRequest
nvtt::CompressionOptions compressionOptions;
nvtt::OutputOptions outputOptions;
bool isDXT1a; // see comment in RunThread
bool is8bpp;
};
/**
@ -148,6 +149,8 @@ CTextureConverter::SettingsFile* CTextureConverter::LoadSettings(const VfsPath&
p.settings.format = FMT_DXT5;
else if (v == "rgba")
p.settings.format = FMT_RGBA;
else if (v == "alpha")
p.settings.format = FMT_ALPHA;
else
LOGERROR(L"Invalid attribute value <file format='%hs'>", v.c_str());
}
@ -337,8 +340,21 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath
// Check whether there's any alpha channel
bool hasAlpha = ((tex.flags & TEX_ALPHA) != 0);
if (settings.format == FMT_ALPHA)
{
// Convert to uncompressed 8-bit with no mipmaps
if (tex_transform_to(&tex, (tex.flags | TEX_GREY) & ~(TEX_DXT | TEX_MIPMAPS | TEX_ALPHA)) < 0)
{
LOGERROR(L"Failed to transform texture \"%ls\"", src.string().c_str());
tex_free(&tex);
return false;
}
}
else
{
// TODO: grayscale images will fail on some systems
// see http://trac.wildfiregames.com/ticket/1640
// (plain_transform doesn't know how to construct the alpha channel)
if (tex.flags & TEX_GREY)
{
LOGERROR(L"Failed to convert grayscale texture \"%ls\" - only RGB textures are currently supported", src.string().c_str());
@ -352,6 +368,7 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath
tex_free(&tex);
return false;
}
}
// Check if the texture has all alpha=255, so we can automatically
// switch from DXT3/DXT5 to DXT1 with no loss
@ -385,6 +402,7 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath
request->inputOptions.setAlphaMode(nvtt::AlphaMode_None);
request->isDXT1a = false;
request->is8bpp = false;
if (settings.format == FMT_RGBA)
{
@ -392,6 +410,12 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath
// Change the default component order (see tex_dds.cpp decode_pf)
request->compressionOptions.setPixelFormat(32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000u);
}
else if (settings.format == FMT_ALPHA)
{
request->compressionOptions.setFormat(nvtt::Format_RGBA);
request->compressionOptions.setPixelFormat(8, 0x00, 0x00, 0x00, 0xFF);
request->is8bpp = true;
}
else if (!hasAlpha)
{
// if no alpha channel then there's no point using DXT3 or DXT5
@ -431,7 +455,24 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath
// Load the texture data
request->inputOptions.setTextureLayout(nvtt::TextureType_2D, tex.w, tex.h);
if (tex.bpp == 32)
{
request->inputOptions.setMipmapData(tex_get_data(&tex), tex.w, tex.h);
}
else // bpp == 8
{
// NVTT requires 32-bit input data, so convert
const u8* input = tex_get_data(&tex);
u8* rgba = new u8[tex.w * tex.h * 4];
u8* p = rgba;
for (size_t i = 0; i < tex.w * tex.h; i++)
{
p[0] = p[1] = p[2] = p[3] = *input++;
p += 4;
}
request->inputOptions.setMipmapData(rgba, tex.w, tex.h);
delete[] rgba;
}
// NVTT copies the texture data so we can free it now
tex_free(&tex);
@ -562,6 +603,10 @@ void* CTextureConverter::RunThread(void* data)
// set the flag here.
if (request->isDXT1a && result->ret && result->output.buffer.size() > 80)
result->output.buffer[80] |= 1; // DDPF_ALPHAPIXELS in DDS_PIXELFORMAT.dwFlags
// Ugly hack: NVTT always sets DDPF_RGB, even if we're trying to output 8-bit
// alpha-only DDS with no RGB components. Unset that flag.
if (request->is8bpp)
result->output.buffer[80] &= ~0x40; // DDPF_RGB in DDS_PIXELFORMAT.dwFlags
// Push the result onto the queue
pthread_mutex_lock(&textureConverter->m_WorkerMutex);

View File

@ -69,7 +69,8 @@ public:
FMT_DXT1,
FMT_DXT3,
FMT_DXT5,
FMT_RGBA
FMT_RGBA,
FMT_ALPHA
};
enum EMipmap

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2012 Wildfire Games.
/* Copyright (C) 2013 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -48,6 +48,7 @@ struct TPhash
boost::hash_combine(seed, a.m_WrapS);
boost::hash_combine(seed, a.m_WrapT);
boost::hash_combine(seed, a.m_Aniso);
boost::hash_combine(seed, a.m_Format);
return seed;
}
std::size_t operator()(CTexturePtr const& a) const
@ -64,7 +65,7 @@ struct TPequal_to
{
return a.m_Path == b.m_Path && a.m_Filter == b.m_Filter
&& a.m_WrapS == b.m_WrapS && a.m_WrapT == b.m_WrapT
&& a.m_Aniso == b.m_Aniso;
&& a.m_Aniso == b.m_Aniso && a.m_Format == b.m_Format;
}
bool operator()(CTexturePtr const& a, CTexturePtr const& b) const
{
@ -215,7 +216,7 @@ public:
(void)ogl_tex_set_filter(h, filter);
// Upload to GL
if (!m_DisableGL && ogl_tex_upload(h) < 0)
if (!m_DisableGL && ogl_tex_upload(h, texture->m_Properties.m_Format) < 0)
{
LOGERROR(L"Texture failed to upload: \"%ls\"", texture->m_Properties.m_Path.string().c_str());

View File

@ -132,7 +132,7 @@ public:
*/
explicit CTextureProperties(const VfsPath& path) :
m_Path(path), m_Filter(GL_LINEAR_MIPMAP_LINEAR),
m_WrapS(GL_REPEAT), m_WrapT(GL_REPEAT), m_Aniso(1.0f)
m_WrapS(GL_REPEAT), m_WrapT(GL_REPEAT), m_Aniso(1.0f), m_Format(0)
{
}
@ -157,6 +157,12 @@ public:
*/
void SetMaxAnisotropy(float aniso) { m_Aniso = aniso; }
/**
* Set GL texture upload format, to override the default.
* Typically GL_ALPHA or GL_LUMINANCE for 8-bit textures.
*/
void SetFormatOverride(GLenum format) { m_Format = format; }
// TODO: rather than this static definition of texture properties
// (especially anisotropy), maybe we want something that can be more
// easily tweaked in an Options menu? e.g. the caller just specifies
@ -175,11 +181,13 @@ public:
// or something a bit like that.
private:
// Must update TPhash, TPequal_to when changing these fields
VfsPath m_Path;
GLint m_Filter;
GLint m_WrapS;
GLint m_WrapT;
float m_Aniso;
GLenum m_Format;
};
/**

View File

@ -398,7 +398,7 @@ static Status decode_pf(const DDS_PIXELFORMAT* pf, size_t& bpp, size_t& flags)
// determine type
const size_t pf_flags = (size_t)read_le32(&pf->dwFlags);
// .. uncompressed
// .. uncompressed RGB/RGBA
if(pf_flags & DDPF_RGB)
{
const size_t pf_bpp = (size_t)read_le32(&pf->dwRGBBitCount);
@ -415,7 +415,7 @@ static Status decode_pf(const DDS_PIXELFORMAT* pf, size_t& bpp, size_t& flags)
{
// something weird other than RGBA or BGRA
if(pf_a_mask != 0xFF000000)
goto unsupported_component_ordering;
WARN_RETURN(ERR::TEX_FMT_INVALID);
flags |= TEX_ALPHA;
}
@ -429,12 +429,28 @@ static Status decode_pf(const DDS_PIXELFORMAT* pf, size_t& bpp, size_t& flags)
// DDS is storing images in a format that requires no processing,
// we do not allow any weird orderings that require runtime work.
// instead, the artists must export with the correct settings.
unsupported_component_ordering:
WARN_RETURN(ERR::TEX_FMT_INVALID);
}
RETURN_STATUS_IF_ERR(tex_validate_plain_format(bpp, (int)flags));
}
// .. uncompressed 8bpp greyscale
else if(pf_flags & DDPF_ALPHAPIXELS)
{
const size_t pf_bpp = (size_t)read_le32(&pf->dwRGBBitCount);
const size_t pf_a_mask = (size_t)read_le32(&pf->dwABitMask);
bpp = pf_bpp;
if(pf_bpp != 8)
WARN_RETURN(ERR::TEX_FMT_INVALID);
if(pf_a_mask != 0xFF)
WARN_RETURN(ERR::TEX_FMT_INVALID);
flags |= TEX_GREY;
RETURN_STATUS_IF_ERR(tex_validate_plain_format(bpp, (int)flags));
}
// .. compressed
else if(pf_flags & DDPF_FOURCC)
{