1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-10 03:39:55 +00:00

Modernize NiPixelData

This commit is contained in:
Alexei Kotov 2023-09-03 20:49:09 +03:00
parent 523e7e8228
commit 5e8f9e7dd9
3 changed files with 176 additions and 110 deletions

View File

@ -224,58 +224,61 @@ namespace Nif
mKeyList->read(nif);
}
void NiPixelFormat::read(NIFStream* nif)
{
mFormat = static_cast<Format>(nif->get<uint32_t>());
if (nif->getVersion() <= NIFStream::generateVersion(10, 4, 0, 1))
{
nif->readArray(mColorMasks);
nif->read(mBitsPerPixel);
nif->readArray(mCompareBits);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
nif->read(mPixelTiling);
}
else
{
mBitsPerPixel = nif->get<uint8_t>();
nif->read(mRendererHint);
nif->read(mExtraData);
nif->read(mFlags);
nif->read(mPixelTiling);
if (nif->getVersion() >= NIFStream::generateVersion(20, 3, 0, 4))
nif->read(mUseSrgb);
for (int i = 0; i < 4; i++)
mChannels[i].read(nif);
}
}
void NiPixelFormat::ChannelData::read(NIFStream* nif)
{
mType = static_cast<Type>(nif->get<uint32_t>());
mConvention = static_cast<Convention>(nif->get<uint32_t>());
nif->read(mBitsPerChannel);
nif->read(mSigned);
}
void NiPixelData::read(NIFStream* nif)
{
fmt = (Format)nif->getUInt();
if (nif->getVersion() < NIFStream::generateVersion(10, 4, 0, 2))
mPixelFormat.read(nif);
mPalette.read(nif);
mMipmaps.resize(nif->get<uint32_t>());
nif->read(mBytesPerPixel);
for (Mipmap& mip : mMipmaps)
{
for (unsigned int i = 0; i < 4; ++i)
colorMask[i] = nif->getUInt();
bpp = nif->getUInt();
nif->skip(8); // "Old Fast Compare". Whatever that means.
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
pixelTiling = nif->getUInt();
nif->read(mip.mWidth);
nif->read(mip.mHeight);
nif->read(mip.mOffset);
}
else // TODO: see if anything from here needs to be implemented
{
bpp = nif->getChar();
nif->skip(4); // Renderer hint
nif->skip(4); // Extra data
nif->skip(4); // Flags
pixelTiling = nif->getUInt();
if (nif->getVersion() >= NIFStream::generateVersion(20, 3, 0, 4))
sRGB = nif->getBoolean();
nif->skip(4 * 10); // Channel data
}
palette.read(nif);
numberOfMipmaps = nif->getUInt();
// Bytes per pixel, should be bpp / 8
/* int bytes = */ nif->getUInt();
for (unsigned int i = 0; i < numberOfMipmaps; i++)
{
// Image size and offset in the following data field
Mipmap m;
m.width = nif->getUInt();
m.height = nif->getUInt();
m.dataOffset = nif->getUInt();
mipmaps.push_back(m);
}
// Read the data
unsigned int numPixels = nif->getUInt();
bool hasFaces = nif->getVersion() >= NIFStream::generateVersion(10, 4, 0, 2);
unsigned int numFaces = hasFaces ? nif->getUInt() : 1;
nif->readVector(data, numPixels * numFaces);
uint32_t numPixels;
nif->read(numPixels);
if (nif->getVersion() >= NIFStream::generateVersion(10, 4, 0, 2))
nif->read(mNumFaces);
nif->readVector(mData, numPixels * mNumFaces);
}
void NiPixelData::post(Reader& nif)
{
palette.post(nif);
mPalette.post(nif);
}
void NiColorData::read(NIFStream* nif)

View File

@ -116,37 +116,94 @@ namespace Nif
void read(NIFStream* nif) override;
};
struct NiPixelFormat
{
enum class Format : uint32_t
{
RGB = 0,
RGBA = 1,
Palette = 2,
PaletteAlpha = 3,
BGR = 4,
BGRA = 5,
DXT1 = 6,
DXT3 = 7,
DXT5 = 8,
};
struct ChannelData
{
enum class Type : uint32_t
{
Red = 0,
Green = 1,
Blue = 2,
Alpha = 3,
Compressed = 4,
OffsetU = 5,
OffsetV = 6,
OffsetW = 7,
OffsetQ = 8,
Luma = 9,
Height = 10,
VectorX = 11,
VectorY = 12,
VectorZ = 13,
Padding = 14,
Intensity = 15,
Index = 16,
Depth = 17,
Stencil = 18,
Empty = 19,
};
enum class Convention : uint32_t
{
NormInt = 0,
Half = 1,
Float = 2,
Index = 3,
Compressed = 4,
Unknown = 5,
Int = 6,
};
Type mType;
Convention mConvention;
uint8_t mBitsPerChannel;
bool mSigned;
void read(NIFStream* nif);
};
Format mFormat{ Format::RGB };
std::array<uint32_t, 4> mColorMasks;
uint32_t mBitsPerPixel{ 0 };
uint32_t mPixelTiling{ 0 };
std::array<uint32_t, 2> mCompareBits;
uint32_t mRendererHint{ 0 };
uint32_t mExtraData{ 0 };
uint8_t mFlags{ 0 };
bool mUseSrgb{ false };
std::array<ChannelData, 4> mChannels;
void read(NIFStream* nif);
};
struct NiPixelData : public Record
{
enum Format
{
NIPXFMT_RGB8,
NIPXFMT_RGBA8,
NIPXFMT_PAL8,
NIPXFMT_PALA8,
NIPXFMT_BGR8,
NIPXFMT_BGRA8,
NIPXFMT_DXT1,
NIPXFMT_DXT3,
NIPXFMT_DXT5
};
Format fmt{ NIPXFMT_RGB8 };
unsigned int colorMask[4]{ 0 };
unsigned int bpp{ 0 }, pixelTiling{ 0 };
bool sRGB{ false };
NiPalettePtr palette;
unsigned int numberOfMipmaps{ 0 };
struct Mipmap
{
int width, height;
int dataOffset;
uint32_t mWidth, mHeight;
uint32_t mOffset;
};
std::vector<Mipmap> mipmaps;
std::vector<unsigned char> data;
NiPixelFormat mPixelFormat;
NiPalettePtr mPalette;
uint32_t mBytesPerPixel;
std::vector<Mipmap> mMipmaps;
uint32_t mNumFaces{ 1 };
std::vector<uint8_t> mData;
void read(NIFStream* nif) override;
void post(Reader& nif) override;

View File

@ -1680,71 +1680,76 @@ namespace NifOsg
osg::ref_ptr<osg::Image> handleInternalTexture(const Nif::NiPixelData* pixelData)
{
osg::ref_ptr<osg::Image> image(new osg::Image);
if (pixelData->mMipmaps.empty())
return nullptr;
// Pixel row alignment, defining it to be consistent with OSG DDS plugin
int packing = 1;
// Not fatal, but warn the user
if (pixelData->mNumFaces != 1)
Log(Debug::Info) << "Unsupported multifaceted internal texture in " << mFilename;
using Nif::NiPixelFormat;
NiPixelFormat niPixelFormat = pixelData->mPixelFormat;
GLenum pixelformat = 0;
switch (pixelData->fmt)
// Pixel row alignment. Defining it to be consistent with OSG DDS plugin
int packing = 1;
switch (niPixelFormat.mFormat)
{
case Nif::NiPixelData::NIPXFMT_RGB8:
case NiPixelFormat::Format::RGB:
pixelformat = GL_RGB;
break;
case Nif::NiPixelData::NIPXFMT_RGBA8:
case NiPixelFormat::Format::RGBA:
pixelformat = GL_RGBA;
break;
case Nif::NiPixelData::NIPXFMT_PAL8:
case Nif::NiPixelData::NIPXFMT_PALA8:
case NiPixelFormat::Format::Palette:
case NiPixelFormat::Format::PaletteAlpha:
pixelformat = GL_RED; // Each color is defined by a byte.
break;
case Nif::NiPixelData::NIPXFMT_BGR8:
case NiPixelFormat::Format::BGR:
pixelformat = GL_BGR;
break;
case Nif::NiPixelData::NIPXFMT_BGRA8:
case NiPixelFormat::Format::BGRA:
pixelformat = GL_BGRA;
break;
case Nif::NiPixelData::NIPXFMT_DXT1:
case NiPixelFormat::Format::DXT1:
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
packing = 2;
break;
case Nif::NiPixelData::NIPXFMT_DXT3:
case NiPixelFormat::Format::DXT3:
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
packing = 4;
break;
case Nif::NiPixelData::NIPXFMT_DXT5:
case NiPixelFormat::Format::DXT5:
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
packing = 4;
break;
default:
Log(Debug::Info) << "Unhandled internal pixel format " << pixelData->fmt << " in " << mFilename;
Log(Debug::Info) << "Unhandled internal pixel format "
<< static_cast<uint32_t>(niPixelFormat.mFormat) << " in " << mFilename;
return nullptr;
}
if (pixelData->mipmaps.empty())
return nullptr;
int width = 0;
int height = 0;
std::vector<unsigned int> mipmapVector;
for (unsigned int i = 0; i < pixelData->mipmaps.size(); ++i)
std::vector<unsigned int> mipmapOffsets;
for (unsigned int i = 0; i < pixelData->mMipmaps.size(); ++i)
{
const Nif::NiPixelData::Mipmap& mip = pixelData->mipmaps[i];
const Nif::NiPixelData::Mipmap& mip = pixelData->mMipmaps[i];
size_t mipSize = osg::Image::computeImageSizeInBytes(
mip.width, mip.height, 1, pixelformat, GL_UNSIGNED_BYTE, packing);
if (mipSize + mip.dataOffset > pixelData->data.size())
mip.mWidth, mip.mHeight, 1, pixelformat, GL_UNSIGNED_BYTE, packing);
if (mipSize + mip.mOffset > pixelData->mData.size())
{
Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture";
return nullptr;
}
if (i != 0)
mipmapVector.push_back(mip.dataOffset);
mipmapOffsets.push_back(mip.mOffset);
else
{
width = mip.width;
height = mip.height;
width = mip.mWidth;
height = mip.mHeight;
}
}
@ -1754,16 +1759,17 @@ namespace NifOsg
return nullptr;
}
const std::vector<unsigned char>& pixels = pixelData->data;
switch (pixelData->fmt)
osg::ref_ptr<osg::Image> image(new osg::Image);
const std::vector<unsigned char>& pixels = pixelData->mData;
switch (niPixelFormat.mFormat)
{
case Nif::NiPixelData::NIPXFMT_RGB8:
case Nif::NiPixelData::NIPXFMT_RGBA8:
case Nif::NiPixelData::NIPXFMT_BGR8:
case Nif::NiPixelData::NIPXFMT_BGRA8:
case Nif::NiPixelData::NIPXFMT_DXT1:
case Nif::NiPixelData::NIPXFMT_DXT3:
case Nif::NiPixelData::NIPXFMT_DXT5:
case NiPixelFormat::Format::RGB:
case NiPixelFormat::Format::RGBA:
case NiPixelFormat::Format::BGR:
case NiPixelFormat::Format::BGRA:
case NiPixelFormat::Format::DXT1:
case NiPixelFormat::Format::DXT3:
case NiPixelFormat::Format::DXT5:
{
unsigned char* data = new unsigned char[pixels.size()];
memcpy(data, pixels.data(), pixels.size());
@ -1771,18 +1777,18 @@ namespace NifOsg
osg::Image::USE_NEW_DELETE, packing);
break;
}
case Nif::NiPixelData::NIPXFMT_PAL8:
case Nif::NiPixelData::NIPXFMT_PALA8:
case NiPixelFormat::Format::Palette:
case NiPixelFormat::Format::PaletteAlpha:
{
if (pixelData->palette.empty() || pixelData->bpp != 8)
if (pixelData->mPalette.empty() || niPixelFormat.mBitsPerPixel != 8)
{
Log(Debug::Info) << "Palettized texture in " << mFilename << " is invalid, ignoring";
return nullptr;
}
pixelformat = pixelData->fmt == Nif::NiPixelData::NIPXFMT_PAL8 ? GL_RGB : GL_RGBA;
pixelformat = niPixelFormat.mFormat == NiPixelFormat::Format::PaletteAlpha ? GL_RGBA : GL_RGB;
// We're going to convert the indices that pixel data contains
// into real colors using the palette.
const auto& palette = pixelData->palette->mColors;
const auto& palette = pixelData->mPalette->mColors;
const int numChannels = pixelformat == GL_RGBA ? 4 : 3;
unsigned char* data = new unsigned char[pixels.size() * numChannels];
unsigned char* pixel = data;
@ -1791,7 +1797,7 @@ namespace NifOsg
memcpy(pixel, &palette[index], sizeof(unsigned char) * numChannels);
pixel += numChannels;
}
for (unsigned int& offset : mipmapVector)
for (unsigned int& offset : mipmapOffsets)
offset *= numChannels;
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data,
osg::Image::USE_NEW_DELETE, packing);
@ -1801,7 +1807,7 @@ namespace NifOsg
return nullptr;
}
image->setMipmapLevels(mipmapVector);
image->setMipmapLevels(mipmapOffsets);
image->flipVertical();
return image;