From 5e8f9e7dd971599f107f4e557f789e24587a1ac1 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 3 Sep 2023 20:49:09 +0300 Subject: [PATCH] Modernize NiPixelData --- components/nif/data.cpp | 91 ++++++++++++++------------- components/nif/data.hpp | 107 ++++++++++++++++++++++++-------- components/nifosg/nifloader.cpp | 88 ++++++++++++++------------ 3 files changed, 176 insertions(+), 110 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 6086a07aa9..ed17de9464 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -224,58 +224,61 @@ namespace Nif mKeyList->read(nif); } + void NiPixelFormat::read(NIFStream* nif) + { + mFormat = static_cast(nif->get()); + 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(); + 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(nif->get()); + mConvention = static_cast(nif->get()); + 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()); + 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) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index d9f118f0fe..0b62071a98 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -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 mColorMasks; + uint32_t mBitsPerPixel{ 0 }; + uint32_t mPixelTiling{ 0 }; + std::array mCompareBits; + uint32_t mRendererHint{ 0 }; + uint32_t mExtraData{ 0 }; + uint8_t mFlags{ 0 }; + bool mUseSrgb{ false }; + std::array 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 mipmaps; - std::vector data; + NiPixelFormat mPixelFormat; + NiPalettePtr mPalette; + uint32_t mBytesPerPixel; + std::vector mMipmaps; + uint32_t mNumFaces{ 1 }; + std::vector mData; void read(NIFStream* nif) override; void post(Reader& nif) override; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index f34fc4591f..d9e3dc1439 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1680,71 +1680,76 @@ namespace NifOsg osg::ref_ptr handleInternalTexture(const Nif::NiPixelData* pixelData) { - osg::ref_ptr 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(niPixelFormat.mFormat) << " in " << mFilename; return nullptr; } - if (pixelData->mipmaps.empty()) - return nullptr; - int width = 0; int height = 0; - std::vector mipmapVector; - for (unsigned int i = 0; i < pixelData->mipmaps.size(); ++i) + std::vector 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& pixels = pixelData->data; - switch (pixelData->fmt) + osg::ref_ptr image(new osg::Image); + const std::vector& 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;