From 74aa7b947ac19c40818e3a495895234451847f25 Mon Sep 17 00:00:00 2001 From: alekulyn Date: Sun, 9 Jul 2023 10:14:27 +0000 Subject: [PATCH] Load BSTriShape NIF nodes --- components/misc/float16.hpp | 11 +++ components/nif/data.cpp | 24 +++++- components/nif/data.hpp | 11 ++- components/nif/niffile.cpp | 1 + components/nif/niffile.hpp | 4 +- components/nif/nifstream.hpp | 14 +++- components/nif/node.cpp | 153 ++++++++++++++++++++++++++++++++++- components/nif/node.hpp | 77 ++++++++++++++++++ components/nif/record.hpp | 1 + 9 files changed, 287 insertions(+), 9 deletions(-) create mode 100644 components/misc/float16.hpp diff --git a/components/misc/float16.hpp b/components/misc/float16.hpp new file mode 100644 index 0000000000..fc1a24a42a --- /dev/null +++ b/components/misc/float16.hpp @@ -0,0 +1,11 @@ +#ifndef OPENMW_COMPONENTS_MISC_FLOAT16_HPP +#define OPENMW_COMPONENTS_MISC_FLOAT16_HPP + +#include + +namespace Misc +{ + using float16_t = std::uint16_t; +} + +#endif diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 54fdea6f8c..cba34d9a6d 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -372,9 +372,21 @@ namespace Nif void NiSkinPartition::read(NIFStream* nif) { - unsigned int num = nif->getUInt(); - data.resize(num); - for (auto& partition : data) + nif->read(mPartitionNum); + mPartitions.resize(mPartitionNum); + + 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); } @@ -426,6 +438,12 @@ namespace Nif nif->getChar(); // LOD level nif->getBoolean(); // Global VB } + + if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) + { + mVertexDesc.read(nif); + nif->readVector(trueTriangles, numTriangles * 3); + } } void NiMorphData::read(NIFStream* nif) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 96e8441639..ef02889d60 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -27,6 +27,7 @@ #include "nifkey.hpp" #include "niftypes.hpp" // Transformation #include "recordptr.hpp" +#include namespace Nif { @@ -218,10 +219,18 @@ namespace Nif std::vector weights; std::vector> strips; std::vector triangles; + std::vector trueTriangles; std::vector boneIndices; + BSVertexDesc mVertexDesc; void read(NIFStream* nif); }; - std::vector data; + unsigned int mPartitionNum; + std::vector mPartitions; + + unsigned int mDataSize; + unsigned int mVertexSize; + BSVertexDesc mVertexDesc; + std::vector mVertexData; void read(NIFStream* nif) override; }; diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 9ab2f5fa75..ce5a42f5db 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -191,6 +191,7 @@ namespace Nif { "BSMultiBoundOBB", &construct }, { "BSMultiBoundSphere", &construct }, { "BSInvMarker", &construct }, + { "BSTriShape", &construct }, }; } diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 96534df2d3..154ab6b140 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -28,7 +28,9 @@ namespace Nif { BETHVER_FO3 = 34, // Fallout 3 BETHVER_SKY = 83, // Skyrim - BETHVER_FO4 = 130 // Fallout 4 + BETHVER_SSE = 100, // Skyrim SE + BETHVER_FO4 = 130, // Fallout 4 + BETHVER_F76 = 155 // Fallout 76 }; /// File version, user version, Bethesda version diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 27473b82f8..e4ad8727c3 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -24,10 +25,16 @@ namespace Nif class Reader; + template + struct is_arithmetic + : std::integral_constant || std::is_same::value> + { + }; + template inline void readLittleEndianBufferOfType(Files::IStreamPtr& pIStream, T* dest) { - static_assert(std::is_arithmetic_v, "Buffer element type is not arithmetic"); + static_assert(is_arithmetic(), "Buffer element type is not arithmetic"); pIStream->read((char*)dest, numInstances * sizeof(T)); if (pIStream->bad()) throw std::runtime_error("Failed to read little endian typed (" + std::string(typeid(T).name()) @@ -40,7 +47,7 @@ namespace Nif template inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr& pIStream, T* dest, std::size_t numInstances) { - static_assert(std::is_arithmetic_v, "Buffer element type is not arithmetic"); + static_assert(is_arithmetic(), "Buffer element type is not arithmetic"); pIStream->read((char*)dest, numInstances * sizeof(T)); if (pIStream->bad()) throw std::runtime_error( @@ -81,6 +88,8 @@ namespace Nif data = readLittleEndianType(inp); } + void read(osg::Vec3f& data) { readLittleEndianBufferOfType<3, float>(inp, data._v); } + template T get() { @@ -125,6 +134,7 @@ namespace Nif return vec; } + // DEPRECATED: Use read() whenever relevant osg::Vec3f getVector3() { osg::Vec3f vec; diff --git a/components/nif/node.cpp b/components/nif/node.cpp index 0ec1b8ab33..09e1e1589d 100644 --- a/components/nif/node.cpp +++ b/components/nif/node.cpp @@ -1,6 +1,7 @@ #include "node.hpp" #include +#include #include "data.hpp" #include "exception.hpp" @@ -18,8 +19,7 @@ namespace Nif break; case SPHERE_BV: { - sphere.center = nif->getVector3(); - sphere.radius = nif->getFloat(); + sphere.read(nif); break; } case BOX_BV: @@ -334,4 +334,153 @@ namespace Nif NiNode::post(nif); mMultiBound.post(nif); } + + void BSTriShape::read(NIFStream* nif) + { + Node::read(nif); + mBoundingSphere.read(nif); + + if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76) + { + nif->readVector(mBoundMinMax, 6); + } + + mSkin.read(nif); + mShaderProperty.read(nif); + mAlphaProperty.read(nif); + + mVertDesc.read(nif); + + unsigned int triNum; + if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4) + { + triNum = nif->get(); + } + else + { + nif->read(triNum); + } + + unsigned short vertNum; + nif->read(vertNum); + nif->read(mDataSize); + + if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) + { + mVertData.resize(vertNum); + for (auto& vertex : mVertData) + vertex.read(nif, mVertDesc.mFlags); + } + else if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4) + { + throw Nif::Exception("FO4 BSTriShape is not supported yet: ", nif->getFile().getFilename()); + } + + if (mDataSize > 0) + { + mTriangles.resize(triNum * 3); + nif->readVector(mTriangles, triNum * 3); + } + + if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) + { + nif->read(mParticleDataSize); + if (mParticleDataSize > 0) + { + throw Nif::Exception("Unhandled Particle Data in BSTriShape: ", nif->getFile().getFilename()); + } + } + } + + void BSTriShape::post(Reader& nif) + { + Node::post(nif); + mSkin.post(nif); + mShaderProperty.post(nif); + mAlphaProperty.post(nif); + } + + void BSVertexDesc::read(NIFStream* nif) + { + uint64_t data; + nif->read(data); + mVertexDataSize = (data & 0xF) >> 0x00; + mDynamicVertexSize = (data & 0xF0) >> 0x04; + mUV1Offset = (data & 0xF00) >> 0x08; + mUV2Offset = (data & 0xF000) >> 0x0C; + mNormalOffset = (data & 0xF0000) >> 0x10; + mTangentOffset = (data & 0xF00000) >> 0x14; + mColorOffset = (data & 0xF000000) >> 0x18; + mSkinningDataOffset = (data & 0xF0000000) >> 0x1C; + mLandscapeDataOffset = (data & 0xF00000000) >> 0x20; + mEyeDataOffset = (data & 0xF000000000) >> 0x24; + mFlags = (data & 0xFFF00000000000) >> 0x2C; + } + + void NiBoundingVolume::NiSphereBV::read(NIFStream* nif) + { + nif->read(center); + nif->read(radius); + } + + void BSVertexData::read(NIFStream* nif, uint16_t flags) + { + uint16_t vertexFlag = flags & BSVertexDesc::VertexAttribute::Vertex; + uint16_t tangentsFlag = flags & BSVertexDesc::VertexAttribute::Tangents; + uint16_t UVsFlag = flags & BSVertexDesc::VertexAttribute::UVs; + uint16_t normalsFlag = flags & BSVertexDesc::VertexAttribute::Normals; + + if (vertexFlag == BSVertexDesc::VertexAttribute::Vertex) + { + nif->read(mVertex); + } + + if ((vertexFlag | tangentsFlag) + == (BSVertexDesc::VertexAttribute::Vertex | BSVertexDesc::VertexAttribute::Tangents)) + { + nif->read(mBitangentX); + } + + if ((vertexFlag | tangentsFlag) == BSVertexDesc::VertexAttribute::Vertex) + { + nif->read(mUnusedW); + } + + if (UVsFlag == BSVertexDesc::VertexAttribute::UVs) + { + nif->readArray(mUV); + } + + if (normalsFlag) + { + nif->readArray(mNormal); + + nif->read(mBitangentY); + } + + if ((normalsFlag | tangentsFlag) + == (BSVertexDesc::VertexAttribute::Normals | BSVertexDesc::VertexAttribute::Tangents)) + { + nif->readArray(mTangent); + + nif->read(mBitangentZ); + } + + if (flags & BSVertexDesc::VertexAttribute::Vertex_Colors) + { + nif->readArray(mVertColors); + } + + if (flags & BSVertexDesc::VertexAttribute::Skinned) + { + nif->readArray(mBoneWeights); + nif->readArray(mBoneIndices); + } + + if (flags & BSVertexDesc::VertexAttribute::Eye_Data) + { + throw Nif::Exception("Unhandled Eye Data in BSTriShape: ", nif->getFile().getFilename()); + nif->read(mEyeData); + } + } } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 8b572a8e7d..c3e0172896 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -1,6 +1,7 @@ #ifndef OPENMW_COMPONENTS_NIF_NODE_HPP #define OPENMW_COMPONENTS_NIF_NODE_HPP +#include #include #include @@ -29,6 +30,7 @@ namespace Nif { osg::Vec3f center; float radius{ 0.f }; + void read(NIFStream* nif); }; struct NiBoxBV @@ -300,5 +302,80 @@ namespace Nif void post(Reader& nif) override; }; + struct BSVertexDesc + { + unsigned char mVertexDataSize; + unsigned char mDynamicVertexSize; + unsigned char mUV1Offset; + unsigned char mUV2Offset; + unsigned char mNormalOffset; + unsigned char mTangentOffset; + unsigned char mColorOffset; + unsigned char mSkinningDataOffset; + unsigned char mLandscapeDataOffset; + unsigned char mEyeDataOffset; + unsigned short mFlags; + + enum VertexAttribute + { + Vertex = 0x0001, + UVs = 0x0002, + UVs_2 = 0x0004, + Normals = 0x0008, + Tangents = 0x0010, + Vertex_Colors = 0x0020, + Skinned = 0x0040, + Land_Data = 0x0080, + Eye_Data = 0x0100, + Instance = 0x0200, + Full_Precision = 0x0400, + }; + + void read(NIFStream* nif); + }; + + struct BSVertexData + { + osg::Vec3f mVertex; + float mBitangentX; + unsigned int mUnusedW; + std::array mUV; + + std::array mNormal; + char mBitangentY; + std::array mTangent; + char mBitangentZ; + std::array mVertColors; + std::array mBoneWeights; + std::array mBoneIndices; + float mEyeData; + + void read(NIFStream* nif, uint16_t flags); + }; + + struct BSTriShape : Node + { + NiBoundingVolume::NiSphereBV mBoundingSphere; + std::vector mBoundMinMax; + + NiSkinInstancePtr mSkin; + BSShaderPropertyPtr mShaderProperty; + NiAlphaPropertyPtr mAlphaProperty; + + BSVertexDesc mVertDesc; + + unsigned int mDataSize; + unsigned int mParticleDataSize; + + std::vector mVertData; + std::vector mTriangles; + std::vector mParticleTriangles; + std::vector mParticleVerts; + std::vector mParticleNormals; + + void read(NIFStream* nif) override; + void post(Reader& nif) override; + }; + } // Namespace #endif diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 550f09c424..88de60ed00 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -163,6 +163,7 @@ namespace Nif RC_BSMultiBoundOBB, RC_BSMultiBoundSphere, RC_BSInvMarker, + RC_BSTriShape, }; /// Base class for all records