mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-28 22:11:34 +00:00
Modernize NiPixelData
This commit is contained in:
parent
523e7e8228
commit
5e8f9e7dd9
3 changed files with 176 additions and 110 deletions
|
@ -224,58 +224,61 @@ namespace Nif
|
||||||
mKeyList->read(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)
|
void NiPixelData::read(NIFStream* nif)
|
||||||
{
|
{
|
||||||
fmt = (Format)nif->getUInt();
|
mPixelFormat.read(nif);
|
||||||
|
mPalette.read(nif);
|
||||||
if (nif->getVersion() < NIFStream::generateVersion(10, 4, 0, 2))
|
mMipmaps.resize(nif->get<uint32_t>());
|
||||||
|
nif->read(mBytesPerPixel);
|
||||||
|
for (Mipmap& mip : mMipmaps)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < 4; ++i)
|
nif->read(mip.mWidth);
|
||||||
colorMask[i] = nif->getUInt();
|
nif->read(mip.mHeight);
|
||||||
bpp = nif->getUInt();
|
nif->read(mip.mOffset);
|
||||||
nif->skip(8); // "Old Fast Compare". Whatever that means.
|
|
||||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
|
|
||||||
pixelTiling = nif->getUInt();
|
|
||||||
}
|
}
|
||||||
else // TODO: see if anything from here needs to be implemented
|
uint32_t numPixels;
|
||||||
{
|
nif->read(numPixels);
|
||||||
bpp = nif->getChar();
|
if (nif->getVersion() >= NIFStream::generateVersion(10, 4, 0, 2))
|
||||||
nif->skip(4); // Renderer hint
|
nif->read(mNumFaces);
|
||||||
nif->skip(4); // Extra data
|
nif->readVector(mData, numPixels * mNumFaces);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiPixelData::post(Reader& nif)
|
void NiPixelData::post(Reader& nif)
|
||||||
{
|
{
|
||||||
palette.post(nif);
|
mPalette.post(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiColorData::read(NIFStream* nif)
|
void NiColorData::read(NIFStream* nif)
|
||||||
|
|
|
@ -116,37 +116,94 @@ namespace Nif
|
||||||
void read(NIFStream* nif) override;
|
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
|
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
|
struct Mipmap
|
||||||
{
|
{
|
||||||
int width, height;
|
uint32_t mWidth, mHeight;
|
||||||
int dataOffset;
|
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 read(NIFStream* nif) override;
|
||||||
void post(Reader& nif) override;
|
void post(Reader& nif) override;
|
||||||
|
|
|
@ -1680,71 +1680,76 @@ namespace NifOsg
|
||||||
|
|
||||||
osg::ref_ptr<osg::Image> handleInternalTexture(const Nif::NiPixelData* pixelData)
|
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
|
// Not fatal, but warn the user
|
||||||
int packing = 1;
|
if (pixelData->mNumFaces != 1)
|
||||||
|
Log(Debug::Info) << "Unsupported multifaceted internal texture in " << mFilename;
|
||||||
|
|
||||||
|
using Nif::NiPixelFormat;
|
||||||
|
NiPixelFormat niPixelFormat = pixelData->mPixelFormat;
|
||||||
GLenum pixelformat = 0;
|
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;
|
pixelformat = GL_RGB;
|
||||||
break;
|
break;
|
||||||
case Nif::NiPixelData::NIPXFMT_RGBA8:
|
case NiPixelFormat::Format::RGBA:
|
||||||
pixelformat = GL_RGBA;
|
pixelformat = GL_RGBA;
|
||||||
break;
|
break;
|
||||||
case Nif::NiPixelData::NIPXFMT_PAL8:
|
case NiPixelFormat::Format::Palette:
|
||||||
case Nif::NiPixelData::NIPXFMT_PALA8:
|
case NiPixelFormat::Format::PaletteAlpha:
|
||||||
pixelformat = GL_RED; // Each color is defined by a byte.
|
pixelformat = GL_RED; // Each color is defined by a byte.
|
||||||
break;
|
break;
|
||||||
case Nif::NiPixelData::NIPXFMT_BGR8:
|
case NiPixelFormat::Format::BGR:
|
||||||
pixelformat = GL_BGR;
|
pixelformat = GL_BGR;
|
||||||
break;
|
break;
|
||||||
case Nif::NiPixelData::NIPXFMT_BGRA8:
|
case NiPixelFormat::Format::BGRA:
|
||||||
pixelformat = GL_BGRA;
|
pixelformat = GL_BGRA;
|
||||||
break;
|
break;
|
||||||
case Nif::NiPixelData::NIPXFMT_DXT1:
|
case NiPixelFormat::Format::DXT1:
|
||||||
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||||
packing = 2;
|
packing = 2;
|
||||||
break;
|
break;
|
||||||
case Nif::NiPixelData::NIPXFMT_DXT3:
|
case NiPixelFormat::Format::DXT3:
|
||||||
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||||
packing = 4;
|
packing = 4;
|
||||||
break;
|
break;
|
||||||
case Nif::NiPixelData::NIPXFMT_DXT5:
|
case NiPixelFormat::Format::DXT5:
|
||||||
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||||
packing = 4;
|
packing = 4;
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pixelData->mipmaps.empty())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
|
|
||||||
std::vector<unsigned int> mipmapVector;
|
std::vector<unsigned int> mipmapOffsets;
|
||||||
for (unsigned int i = 0; i < pixelData->mipmaps.size(); ++i)
|
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(
|
size_t mipSize = osg::Image::computeImageSizeInBytes(
|
||||||
mip.width, mip.height, 1, pixelformat, GL_UNSIGNED_BYTE, packing);
|
mip.mWidth, mip.mHeight, 1, pixelformat, GL_UNSIGNED_BYTE, packing);
|
||||||
if (mipSize + mip.dataOffset > pixelData->data.size())
|
if (mipSize + mip.mOffset > pixelData->mData.size())
|
||||||
{
|
{
|
||||||
Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture";
|
Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i != 0)
|
if (i != 0)
|
||||||
mipmapVector.push_back(mip.dataOffset);
|
mipmapOffsets.push_back(mip.mOffset);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
width = mip.width;
|
width = mip.mWidth;
|
||||||
height = mip.height;
|
height = mip.mHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1754,16 +1759,17 @@ namespace NifOsg
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<unsigned char>& pixels = pixelData->data;
|
osg::ref_ptr<osg::Image> image(new osg::Image);
|
||||||
switch (pixelData->fmt)
|
const std::vector<unsigned char>& pixels = pixelData->mData;
|
||||||
|
switch (niPixelFormat.mFormat)
|
||||||
{
|
{
|
||||||
case Nif::NiPixelData::NIPXFMT_RGB8:
|
case NiPixelFormat::Format::RGB:
|
||||||
case Nif::NiPixelData::NIPXFMT_RGBA8:
|
case NiPixelFormat::Format::RGBA:
|
||||||
case Nif::NiPixelData::NIPXFMT_BGR8:
|
case NiPixelFormat::Format::BGR:
|
||||||
case Nif::NiPixelData::NIPXFMT_BGRA8:
|
case NiPixelFormat::Format::BGRA:
|
||||||
case Nif::NiPixelData::NIPXFMT_DXT1:
|
case NiPixelFormat::Format::DXT1:
|
||||||
case Nif::NiPixelData::NIPXFMT_DXT3:
|
case NiPixelFormat::Format::DXT3:
|
||||||
case Nif::NiPixelData::NIPXFMT_DXT5:
|
case NiPixelFormat::Format::DXT5:
|
||||||
{
|
{
|
||||||
unsigned char* data = new unsigned char[pixels.size()];
|
unsigned char* data = new unsigned char[pixels.size()];
|
||||||
memcpy(data, pixels.data(), pixels.size());
|
memcpy(data, pixels.data(), pixels.size());
|
||||||
|
@ -1771,18 +1777,18 @@ namespace NifOsg
|
||||||
osg::Image::USE_NEW_DELETE, packing);
|
osg::Image::USE_NEW_DELETE, packing);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Nif::NiPixelData::NIPXFMT_PAL8:
|
case NiPixelFormat::Format::Palette:
|
||||||
case Nif::NiPixelData::NIPXFMT_PALA8:
|
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";
|
Log(Debug::Info) << "Palettized texture in " << mFilename << " is invalid, ignoring";
|
||||||
return nullptr;
|
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
|
// We're going to convert the indices that pixel data contains
|
||||||
// into real colors using the palette.
|
// 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;
|
const int numChannels = pixelformat == GL_RGBA ? 4 : 3;
|
||||||
unsigned char* data = new unsigned char[pixels.size() * numChannels];
|
unsigned char* data = new unsigned char[pixels.size() * numChannels];
|
||||||
unsigned char* pixel = data;
|
unsigned char* pixel = data;
|
||||||
|
@ -1791,7 +1797,7 @@ namespace NifOsg
|
||||||
memcpy(pixel, &palette[index], sizeof(unsigned char) * numChannels);
|
memcpy(pixel, &palette[index], sizeof(unsigned char) * numChannels);
|
||||||
pixel += numChannels;
|
pixel += numChannels;
|
||||||
}
|
}
|
||||||
for (unsigned int& offset : mipmapVector)
|
for (unsigned int& offset : mipmapOffsets)
|
||||||
offset *= numChannels;
|
offset *= numChannels;
|
||||||
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data,
|
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data,
|
||||||
osg::Image::USE_NEW_DELETE, packing);
|
osg::Image::USE_NEW_DELETE, packing);
|
||||||
|
@ -1801,7 +1807,7 @@ namespace NifOsg
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
image->setMipmapLevels(mipmapVector);
|
image->setMipmapLevels(mipmapOffsets);
|
||||||
image->flipVertical();
|
image->flipVertical();
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
|
|
Loading…
Reference in a new issue