#include "data.hpp" #include #include "exception.hpp" #include "nifkey.hpp" #include "node.hpp" namespace Nif { void NiGeometryData::read(NIFStream* nif) { if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 114)) nif->read(mGroupId); nif->read(mNumVertices); bool isPSysData = false; switch (recType) { case RC_NiPSysData: case RC_NiMeshPSysData: case RC_BSStripPSysData: isPSysData = true; break; default: break; } bool hasData = !isPSysData || nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO3; if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) { nif->read(mKeepFlags); nif->read(mCompressFlags); } if (nif->get() && hasData) nif->readVector(mVertices, mNumVertices); if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) { nif->read(mDataFlags); if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) nif->read(mMaterialHash); } if (nif->get() && hasData) { nif->readVector(mNormals, mNumVertices); if (mDataFlags & DataFlag_HasTangents) { nif->readVector(mTangents, mNumVertices); nif->readVector(mBitangents, mNumVertices); } } nif->read(mBoundingSphere); if (nif->get() && hasData) nif->readVector(mColors, mNumVertices); if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0)) nif->read(mDataFlags); // In 4.0.0.2 the flags field corresponds to the number of UV sets. // In later revisions the part that corresponds to the number is narrower. uint16_t numUVs = mDataFlags; if (nif->getVersion() > NIFFile::NIFVersion::VER_MW) { numUVs &= DataFlag_NumUVsMask; if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > 0) numUVs &= DataFlag_HasUV; } else if (!nif->get()) numUVs = 0; if (hasData) { mUVList.resize(numUVs); for (std::vector& list : mUVList) { nif->readVector(list, mNumVertices); // flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin for (osg::Vec2f& uv : list) uv.y() = 1.f - uv.y(); } } if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) { nif->read(mConsistencyType); if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4)) nif->skip(4); // Additional data } } void NiTriBasedGeomData::read(NIFStream* nif) { NiGeometryData::read(nif); nif->read(mNumTriangles); } void NiTriShapeData::read(NIFStream* nif) { NiTriBasedGeomData::read(nif); uint32_t numIndices; nif->read(numIndices); if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD && !nif->get()) numIndices = 0; nif->readVector(mTriangles, numIndices); mMatchGroups.resize(nif->get()); for (auto& group : mMatchGroups) nif->readVector(group, nif->get()); } void NiTriStripsData::read(NIFStream* nif) { NiTriBasedGeomData::read(nif); uint16_t numStrips; nif->read(numStrips); std::vector lengths; nif->readVector(lengths, numStrips); if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD && !nif->get()) numStrips = 0; mStrips.resize(numStrips); for (int i = 0; i < numStrips; i++) nif->readVector(mStrips[i], lengths[i]); } void NiLinesData::read(NIFStream* nif) { NiGeometryData::read(nif); std::vector flags; nif->readVector(flags, mNumVertices); // Can't construct a line from a single vertex. if (mNumVertices < 2) return; // There can't be more than 2 indices for each vertex mLines.reserve(mNumVertices * 2); // Convert connectivity flags into usable geometry. The last element needs special handling. for (uint16_t i = 0; i < mNumVertices - 1; ++i) { if (flags[i] & 1) { mLines.emplace_back(i); mLines.emplace_back(i + 1); } } // If there are just two vertices, they can be connected twice. Probably isn't critical. if (flags[mNumVertices - 1] & 1) { mLines.emplace_back(mNumVertices - 1); mLines.emplace_back(0); } mLines.shrink_to_fit(); } void NiPosData::read(NIFStream* nif) { mKeyList = std::make_shared(); mKeyList->read(nif); } void NiUVData::read(NIFStream* nif) { for (FloatKeyMapPtr& keys : mKeyList) { keys = std::make_shared(); keys->read(nif); } } void NiFloatData::read(NIFStream* nif) { mKeyList = std::make_shared(); 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) { mPixelFormat.read(nif); mPalette.read(nif); mMipmaps.resize(nif->get()); nif->read(mBytesPerPixel); for (Mipmap& mip : mMipmaps) { nif->read(mip.mWidth); nif->read(mip.mHeight); nif->read(mip.mOffset); } 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) { mPalette.post(nif); } void NiColorData::read(NIFStream* nif) { mKeyMap = std::make_shared(); mKeyMap->read(nif); } void NiVisData::read(NIFStream* nif) { mKeys = std::make_shared>(); uint32_t numKeys; nif->read(numKeys); for (size_t i = 0; i < numKeys; i++) { float time; char value; nif->read(time); nif->read(value); (*mKeys)[time] = (value != 0); } } void NiSkinInstance::read(NIFStream* nif) { mData.read(nif); if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 101)) mPartitions.read(nif); mRoot.read(nif); readRecordList(nif, mBones); } void NiSkinInstance::post(Reader& nif) { mData.post(nif); mPartitions.post(nif); mRoot.post(nif); postRecordList(nif, mBones); if (mData.empty() || mRoot.empty()) throw Nif::Exception("NiSkinInstance missing root or data", nif.getFilename()); if (mBones.size() != mData->mBones.size()) throw Nif::Exception("Mismatch in NiSkinData bone count", nif.getFilename()); for (auto& bone : mBones) { if (bone.empty()) throw Nif::Exception("Oops: Missing bone! Don't know how to handle this.", nif.getFilename()); bone->setBone(); } } const Nif::NiSkinPartition* NiSkinInstance::getPartitions() const { const Nif::NiSkinPartition* partitions = nullptr; if (!mPartitions.empty()) partitions = mPartitions.getPtr(); else if (!mData.empty() && !mData->mPartitions.empty()) partitions = mData->mPartitions.getPtr(); return partitions; } void BSDismemberSkinInstance::read(NIFStream* nif) { NiSkinInstance::read(nif); mParts.resize(nif->get()); for (BodyPart& part : mParts) { nif->read(part.mFlags); nif->read(part.mType); } } void BSSkinInstance::read(NIFStream* nif) { mRoot.read(nif); mData.read(nif); readRecordList(nif, mBones); nif->readVector(mScales, nif->get()); } void BSSkinInstance::post(Reader& nif) { mRoot.post(nif); mData.post(nif); postRecordList(nif, mBones); if (mData.empty() || mRoot.empty()) throw Nif::Exception("BSSkin::Instance missing root or data", nif.getFilename()); if (mBones.size() != mData->mBones.size()) throw Nif::Exception("Mismatch in BSSkin::BoneData bone count", nif.getFilename()); for (auto& bone : mBones) { if (bone.empty()) throw Nif::Exception("Oops: Missing bone! Don't know how to handle this.", nif.getFilename()); bone->setBone(); } } void NiSkinData::read(NIFStream* nif) { nif->read(mTransform); uint32_t numBones; nif->read(numBones); bool hasVertexWeights = true; if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW) { if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 0)) mPartitions.read(nif); if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0)) nif->read(hasVertexWeights); } mBones.resize(numBones); for (BoneInfo& bi : mBones) { nif->read(bi.mTransform); nif->read(bi.mBoundSphere); uint16_t numVertices; nif->read(numVertices); if (!hasVertexWeights) continue; bi.mWeights.resize(numVertices); for (auto& [vertex, weight] : bi.mWeights) { nif->read(vertex); nif->read(weight); } } } void NiSkinData::post(Reader& nif) { mPartitions.post(nif); } void BSSkinBoneData::read(NIFStream* nif) { mBones.resize(nif->get()); for (BoneInfo& bone : mBones) { nif->read(bone.mBoundSphere); nif->read(bone.mTransform); } } void NiSkinPartition::read(NIFStream* nif) { mPartitions.resize(nif->get()); if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) { nif->read(mDataSize); nif->read(mVertexSize); mVertexDesc.read(nif); mVertexData.resize(mDataSize / mVertexSize); for (auto& vertexData : mVertexData) vertexData.read(nif, mVertexDesc.mFlags); } for (auto& partition : mPartitions) partition.read(nif); } void NiSkinPartition::Partition::read(NIFStream* nif) { uint16_t numVertices, numTriangles, numBones, numStrips, bonesPerVertex; nif->read(numVertices); nif->read(numTriangles); nif->read(numBones); nif->read(numStrips); nif->read(bonesPerVertex); nif->readVector(mBones, numBones); bool hasPresenceFlags = nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0); if (!hasPresenceFlags || nif->get()) nif->readVector(mVertexMap, numVertices); if (!hasPresenceFlags || nif->get()) nif->readVector(mWeights, static_cast(numVertices) * bonesPerVertex); std::vector stripLengths; nif->readVector(stripLengths, numStrips); if (!hasPresenceFlags || nif->get()) { if (numStrips) { mStrips.resize(numStrips); for (size_t i = 0; i < numStrips; i++) nif->readVector(mStrips[i], stripLengths[i]); } else nif->readVector(mTriangles, numTriangles * 3); } if (nif->get() != 0) nif->readVector(mBoneIndices, static_cast(numVertices) * bonesPerVertex); if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) { nif->read(mLODLevel); nif->read(mGlobalVB); if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) { mVertexDesc.read(nif); nif->readVector(mTrueTriangles, numTriangles * 3); } } // Not technically a part of the loading process if (mTrueTriangles.empty() && !mVertexMap.empty()) { if (!mStrips.empty()) { mTrueStrips = mStrips; for (auto& strip : mTrueStrips) for (auto& index : strip) index = mVertexMap[index]; } else if (!mTriangles.empty()) { mTrueTriangles = mTriangles; for (unsigned short& index : mTrueTriangles) index = mVertexMap[index]; } } } void NiMorphData::read(NIFStream* nif) { uint32_t numMorphs, numVerts; nif->read(numMorphs); nif->read(numVerts); nif->read(mRelativeTargets); mMorphs.resize(numMorphs); for (MorphData& morph : mMorphs) { morph.mKeyFrames = std::make_shared(); morph.mKeyFrames->read(nif, /*morph*/ true); nif->readVector(morph.mVertices, numVerts); } } void NiKeyframeData::read(NIFStream* nif) { mRotations = std::make_shared(); mRotations->read(nif); if (mRotations->mInterpolationType == InterpolationType_XYZ) { if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 0)) mAxisOrder = static_cast(nif->get()); mXRotations = std::make_shared(); mYRotations = std::make_shared(); mZRotations = std::make_shared(); mXRotations->read(nif); mYRotations->read(nif); mZRotations->read(nif); } mTranslations = std::make_shared(); mTranslations->read(nif); mScales = std::make_shared(); mScales->read(nif); } void NiPalette::read(NIFStream* nif) { bool useAlpha = nif->get() != 0; uint32_t alphaMask = useAlpha ? 0 : 0xFF000000; uint32_t numEntries; nif->read(numEntries); // Fill the entire palette with black even if there isn't enough entries. mColors.resize(numEntries > 256 ? numEntries : 256); for (uint32_t i = 0; i < numEntries; i++) { nif->read(mColors[i]); mColors[i] |= alphaMask; } } void NiStringPalette::read(NIFStream* nif) { mPalette = nif->getStringPalette(); if (nif->get() != mPalette.size()) Log(Debug::Warning) << "NIFFile Warning: Failed size check in NiStringPalette. File: " << nif->getFile().getFilename(); } void NiBoolData::read(NIFStream* nif) { mKeyList = std::make_shared(); mKeyList->read(nif); } void NiBSplineData::read(NIFStream* nif) { nif->readVector(mFloatControlPoints, nif->get()); nif->readVector(mCompactControlPoints, nif->get()); } void NiBSplineBasisData::read(NIFStream* nif) { nif->read(mNumControlPoints); } void NiAdditionalGeometryData::read(NIFStream* nif) { nif->read(mNumVertices); mBlockInfos.resize(nif->get()); for (DataStream& info : mBlockInfos) info.read(nif); mBlocks.resize(nif->get()); for (DataBlock& block : mBlocks) block.read(nif, recType == RC_BSPackedAdditionalGeometryData); } void NiAdditionalGeometryData::DataStream::read(NIFStream* nif) { nif->read(mType); nif->read(mUnitSize); nif->read(mTotalSize); nif->read(mStride); nif->read(mBlockIndex); nif->read(mBlockOffset); nif->read(mFlags); } void NiAdditionalGeometryData::DataBlock::read(NIFStream* nif, bool bsPacked) { nif->read(mValid); if (!mValid) return; nif->read(mBlockSize); nif->readVector(mBlockOffsets, nif->get()); nif->readVector(mDataSizes, nif->get()); nif->readVector(mData, mDataSizes.size() * mBlockSize); if (bsPacked) { nif->read(mShaderIndex); nif->read(mTotalSize); } } void BSMultiBound::read(NIFStream* nif) { mData.read(nif); } void BSMultiBound::post(Reader& nif) { mData.post(nif); } void BSMultiBoundAABB::read(NIFStream* nif) { nif->read(mPosition); nif->read(mExtents); } void BSMultiBoundOBB::read(NIFStream* nif) { nif->read(mCenter); nif->read(mSize); nif->read(mRotation); } void BSMultiBoundSphere::read(NIFStream* nif) { nif->read(mSphere); } void BSAnimNote::read(NIFStream* nif) { mType = static_cast(nif->get()); nif->read(mTime); if (mType == Type::GrabIK) { nif->read(mArm); } else if (mType == Type::LookIK) { nif->read(mGain); nif->read(mState); } } void BSAnimNotes::read(NIFStream* nif) { mList.resize(nif->get()); for (auto& note : mList) note.read(nif); } void BSAnimNotes::post(Reader& nif) { postRecordList(nif, mList); } } // Namespace