mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-23 16:11:33 +00:00
Merge branch 'allofitjustworks' into 'master'
Fallout 4 and SSE support improvements See merge request OpenMW/openmw!3465
This commit is contained in:
commit
73187b03f3
12 changed files with 434 additions and 135 deletions
|
@ -56,13 +56,26 @@ std::string Misc::ResourceHelpers::correctResourcePath(
|
||||||
|
|
||||||
std::string correctedPath = Misc::StringUtils::lowerCase(resPath);
|
std::string correctedPath = Misc::StringUtils::lowerCase(resPath);
|
||||||
|
|
||||||
// Apparently, leading separators are allowed
|
// Flatten slashes
|
||||||
while (correctedPath.size() && (correctedPath[0] == '/' || correctedPath[0] == '\\'))
|
std::replace(correctedPath.begin(), correctedPath.end(), '/', '\\');
|
||||||
|
auto bothSeparators = [](char a, char b) { return a == '\\' && b == '\\'; };
|
||||||
|
correctedPath.erase(std::unique(correctedPath.begin(), correctedPath.end(), bothSeparators), correctedPath.end());
|
||||||
|
|
||||||
|
// Remove leading separator
|
||||||
|
if (!correctedPath.empty() && correctedPath[0] == '\\')
|
||||||
correctedPath.erase(0, 1);
|
correctedPath.erase(0, 1);
|
||||||
|
|
||||||
|
// Handle top level directory
|
||||||
if (!correctedPath.starts_with(topLevelDirectory) || correctedPath.size() <= topLevelDirectory.size()
|
if (!correctedPath.starts_with(topLevelDirectory) || correctedPath.size() <= topLevelDirectory.size()
|
||||||
|| (correctedPath[topLevelDirectory.size()] != '/' && correctedPath[topLevelDirectory.size()] != '\\'))
|
|| correctedPath[topLevelDirectory.size()] != '\\')
|
||||||
correctedPath = std::string{ topLevelDirectory } + '\\' + correctedPath;
|
{
|
||||||
|
std::string topLevelPrefix = std::string{ topLevelDirectory } + '\\';
|
||||||
|
size_t topLevelPos = correctedPath.find('\\' + topLevelPrefix);
|
||||||
|
if (topLevelPos == std::string::npos)
|
||||||
|
correctedPath = topLevelPrefix + correctedPath;
|
||||||
|
else
|
||||||
|
correctedPath.erase(0, topLevelPos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
std::string origExt = correctedPath;
|
std::string origExt = correctedPath;
|
||||||
|
|
||||||
|
|
|
@ -302,6 +302,33 @@ namespace Nif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BSSkinInstance::read(NIFStream* nif)
|
||||||
|
{
|
||||||
|
mRoot.read(nif);
|
||||||
|
mData.read(nif);
|
||||||
|
readRecordList(nif, mBones);
|
||||||
|
nif->readVector(mScales, nif->get<uint32_t>());
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
void NiSkinData::read(NIFStream* nif)
|
||||||
{
|
{
|
||||||
nif->read(mTransform);
|
nif->read(mTransform);
|
||||||
|
@ -344,6 +371,16 @@ namespace Nif
|
||||||
mPartitions.post(nif);
|
mPartitions.post(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BSSkinBoneData::read(NIFStream* nif)
|
||||||
|
{
|
||||||
|
mBones.resize(nif->get<uint32_t>());
|
||||||
|
for (BoneInfo& bone : mBones)
|
||||||
|
{
|
||||||
|
nif->read(bone.mBoundSphere);
|
||||||
|
nif->read(bone.mTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NiSkinPartition::read(NIFStream* nif)
|
void NiSkinPartition::read(NIFStream* nif)
|
||||||
{
|
{
|
||||||
mPartitions.resize(nif->get<uint32_t>());
|
mPartitions.resize(nif->get<uint32_t>());
|
||||||
|
|
|
@ -223,6 +223,17 @@ namespace Nif
|
||||||
void read(NIFStream* nif) override;
|
void read(NIFStream* nif) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BSSkinInstance : Record
|
||||||
|
{
|
||||||
|
NiAVObjectPtr mRoot;
|
||||||
|
BSSkinBoneDataPtr mData;
|
||||||
|
NiAVObjectList mBones;
|
||||||
|
std::vector<osg::Vec3f> mScales;
|
||||||
|
|
||||||
|
void read(NIFStream* nif) override;
|
||||||
|
void post(Reader& nif) override;
|
||||||
|
};
|
||||||
|
|
||||||
struct NiSkinData : public Record
|
struct NiSkinData : public Record
|
||||||
{
|
{
|
||||||
using VertWeight = std::pair<unsigned short, float>;
|
using VertWeight = std::pair<unsigned short, float>;
|
||||||
|
@ -242,6 +253,19 @@ namespace Nif
|
||||||
void post(Reader& nif) override;
|
void post(Reader& nif) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BSSkinBoneData : Record
|
||||||
|
{
|
||||||
|
struct BoneInfo
|
||||||
|
{
|
||||||
|
osg::BoundingSpheref mBoundSphere;
|
||||||
|
NiTransform mTransform;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<BoneInfo> mBones;
|
||||||
|
|
||||||
|
void read(NIFStream* nif) override;
|
||||||
|
};
|
||||||
|
|
||||||
struct NiSkinPartition : public Record
|
struct NiSkinPartition : public Record
|
||||||
{
|
{
|
||||||
struct Partition
|
struct Partition
|
||||||
|
|
|
@ -136,4 +136,30 @@ namespace Nif
|
||||||
nif->readVector(mData, nif->get<uint32_t>());
|
nif->readVector(mData, nif->get<uint32_t>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BSConnectPoint::Point::read(NIFStream* nif)
|
||||||
|
{
|
||||||
|
mParent = nif->getSizedString();
|
||||||
|
mName = nif->getSizedString();
|
||||||
|
nif->read(mTransform.mRotation);
|
||||||
|
nif->read(mTransform.mTranslation);
|
||||||
|
nif->read(mTransform.mScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSConnectPoint::Parents::read(NIFStream* nif)
|
||||||
|
{
|
||||||
|
NiExtraData::read(nif);
|
||||||
|
|
||||||
|
mPoints.resize(nif->get<uint32_t>());
|
||||||
|
for (Point& point : mPoints)
|
||||||
|
point.read(nif);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSConnectPoint::Children::read(NIFStream* nif)
|
||||||
|
{
|
||||||
|
NiExtraData::read(nif);
|
||||||
|
|
||||||
|
nif->read(mSkinned);
|
||||||
|
nif->getSizedStrings(mPointNames, nif->get<uint32_t>());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,8 @@ namespace Nif
|
||||||
using NiFloatsExtraData = TypedVectorExtra<float>;
|
using NiFloatsExtraData = TypedVectorExtra<float>;
|
||||||
using NiIntegersExtraData = TypedVectorExtra<uint32_t>;
|
using NiIntegersExtraData = TypedVectorExtra<uint32_t>;
|
||||||
|
|
||||||
|
using BSEyeCenterExtraData = TypedVectorExtra<float>;
|
||||||
|
using BSPositionData = TypedVectorExtra<Misc::float16_t>;
|
||||||
using BSWArray = TypedVectorExtra<int32_t>;
|
using BSWArray = TypedVectorExtra<int32_t>;
|
||||||
|
|
||||||
// Distinct from NiBinaryExtraData, uses mRecordSize as its size
|
// Distinct from NiBinaryExtraData, uses mRecordSize as its size
|
||||||
|
@ -170,5 +172,32 @@ namespace Nif
|
||||||
void read(NIFStream* nif) override;
|
void read(NIFStream* nif) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BSConnectPoint
|
||||||
|
{
|
||||||
|
struct Point
|
||||||
|
{
|
||||||
|
std::string mParent;
|
||||||
|
std::string mName;
|
||||||
|
NiQuatTransform mTransform;
|
||||||
|
|
||||||
|
void read(NIFStream* nif);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Parents : NiExtraData
|
||||||
|
{
|
||||||
|
std::vector<Point> mPoints;
|
||||||
|
|
||||||
|
void read(NIFStream* nif) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Children : NiExtraData
|
||||||
|
{
|
||||||
|
bool mSkinned;
|
||||||
|
std::vector<std::string> mPointNames;
|
||||||
|
|
||||||
|
void read(NIFStream* nif) override;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -158,6 +158,7 @@ namespace Nif
|
||||||
&construct<BSEffectShaderPropertyFloatController, RC_BSLightingShaderPropertyFloatController> },
|
&construct<BSEffectShaderPropertyFloatController, RC_BSLightingShaderPropertyFloatController> },
|
||||||
{ "bhkBlendController", &construct<bhkBlendController, RC_bhkBlendController> },
|
{ "bhkBlendController", &construct<bhkBlendController, RC_bhkBlendController> },
|
||||||
{ "NiBSBoneLODController", &construct<NiBoneLODController, RC_NiBoneLODController> },
|
{ "NiBSBoneLODController", &construct<NiBoneLODController, RC_NiBoneLODController> },
|
||||||
|
{ "NiLightRadiusController", &construct<NiFloatInterpController, RC_NiLightRadiusController> },
|
||||||
|
|
||||||
// Interpolators, Gamebryo
|
// Interpolators, Gamebryo
|
||||||
{ "NiBlendBoolInterpolator", &construct<NiBlendBoolInterpolator, RC_NiBlendBoolInterpolator> },
|
{ "NiBlendBoolInterpolator", &construct<NiBlendBoolInterpolator, RC_NiBlendBoolInterpolator> },
|
||||||
|
@ -245,11 +246,15 @@ namespace Nif
|
||||||
{ "BSBehaviorGraphExtraData", &construct<BSBehaviorGraphExtraData, RC_BSBehaviorGraphExtraData> },
|
{ "BSBehaviorGraphExtraData", &construct<BSBehaviorGraphExtraData, RC_BSBehaviorGraphExtraData> },
|
||||||
{ "BSBoneLODExtraData", &construct<BSBoneLODExtraData, RC_BSBoneLODExtraData> },
|
{ "BSBoneLODExtraData", &construct<BSBoneLODExtraData, RC_BSBoneLODExtraData> },
|
||||||
{ "BSClothExtraData", &construct<BSClothExtraData, RC_BSClothExtraData> },
|
{ "BSClothExtraData", &construct<BSClothExtraData, RC_BSClothExtraData> },
|
||||||
|
{ "BSConnectPoint::Children", &construct<BSConnectPoint::Children, RC_BSConnectPointChildren> },
|
||||||
|
{ "BSConnectPoint::Parents", &construct<BSConnectPoint::Parents, RC_BSConnectPointParents> },
|
||||||
{ "BSDecalPlacementVectorExtraData",
|
{ "BSDecalPlacementVectorExtraData",
|
||||||
&construct<BSDecalPlacementVectorExtraData, RC_BSDecalPlacementVectorExtraData> },
|
&construct<BSDecalPlacementVectorExtraData, RC_BSDecalPlacementVectorExtraData> },
|
||||||
{ "BSDistantObjectExtraData", &construct<BSDistantObjectExtraData, RC_BSDistantObjectExtraData> },
|
{ "BSDistantObjectExtraData", &construct<BSDistantObjectExtraData, RC_BSDistantObjectExtraData> },
|
||||||
{ "BSDistantObjectLargeRefExtraData",
|
{ "BSDistantObjectLargeRefExtraData",
|
||||||
&construct<BSDistantObjectLargeRefExtraData, RC_BSDistantObjectLargeRefExtraData> },
|
&construct<BSDistantObjectLargeRefExtraData, RC_BSDistantObjectLargeRefExtraData> },
|
||||||
|
{ "BSEyeCenterExtraData", &construct<BSEyeCenterExtraData, RC_BSEyeCenterExtraData> },
|
||||||
|
{ "BSPositionData", &construct<BSPositionData, RC_BSPositionData> },
|
||||||
{ "BSWArray", &construct<BSWArray, RC_BSWArray> },
|
{ "BSWArray", &construct<BSWArray, RC_BSWArray> },
|
||||||
{ "BSXFlags", &construct<NiIntegerExtraData, RC_BSXFlags> },
|
{ "BSXFlags", &construct<NiIntegerExtraData, RC_BSXFlags> },
|
||||||
|
|
||||||
|
@ -268,6 +273,8 @@ namespace Nif
|
||||||
|
|
||||||
// Bethesda
|
// Bethesda
|
||||||
{ "BSDismemberSkinInstance", &construct<BSDismemberSkinInstance, RC_BSDismemberSkinInstance> },
|
{ "BSDismemberSkinInstance", &construct<BSDismemberSkinInstance, RC_BSDismemberSkinInstance> },
|
||||||
|
{ "BSSkin::Instance", &construct<BSSkinInstance, RC_BSSkinInstance> },
|
||||||
|
{ "BSSkin::BoneData", &construct<BSSkinBoneData, RC_BSSkinBoneData> },
|
||||||
{ "BSTriShape", &construct<BSTriShape, RC_BSTriShape> },
|
{ "BSTriShape", &construct<BSTriShape, RC_BSTriShape> },
|
||||||
{ "BSDynamicTriShape", &construct<BSDynamicTriShape, RC_BSDynamicTriShape> },
|
{ "BSDynamicTriShape", &construct<BSDynamicTriShape, RC_BSDynamicTriShape> },
|
||||||
{ "BSLODTriShape", &construct<BSLODTriShape, RC_BSLODTriShape> },
|
{ "BSLODTriShape", &construct<BSLODTriShape, RC_BSLODTriShape> },
|
||||||
|
|
|
@ -426,6 +426,8 @@ namespace Nif
|
||||||
mSkin.post(nif);
|
mSkin.post(nif);
|
||||||
mShaderProperty.post(nif);
|
mShaderProperty.post(nif);
|
||||||
mAlphaProperty.post(nif);
|
mAlphaProperty.post(nif);
|
||||||
|
if (!mSkin.empty())
|
||||||
|
nif.setUseSkinning(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSDynamicTriShape::read(NIFStream* nif)
|
void BSDynamicTriShape::read(NIFStream* nif)
|
||||||
|
@ -460,14 +462,13 @@ namespace Nif
|
||||||
mLandscapeDataOffset = (data & 0xF00000000) >> 0x20;
|
mLandscapeDataOffset = (data & 0xF00000000) >> 0x20;
|
||||||
mEyeDataOffset = (data & 0xF000000000) >> 0x24;
|
mEyeDataOffset = (data & 0xF000000000) >> 0x24;
|
||||||
mFlags = (data & 0xFFF00000000000) >> 0x2C;
|
mFlags = (data & 0xFFF00000000000) >> 0x2C;
|
||||||
|
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE)
|
||||||
|
mFlags |= BSVertexDesc::VertexAttribute::Full_Precision;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSVertexData::read(NIFStream* nif, uint16_t flags)
|
void BSVertexData::read(NIFStream* nif, uint16_t flags)
|
||||||
{
|
{
|
||||||
bool fullPrecision = true;
|
bool fullPrecision = flags & BSVertexDesc::VertexAttribute::Full_Precision;
|
||||||
if (nif->getBethVersion() != NIFFile::BethVersion::BETHVER_SSE)
|
|
||||||
fullPrecision = flags & BSVertexDesc::VertexAttribute::Full_Precision;
|
|
||||||
|
|
||||||
bool hasVertex = flags & BSVertexDesc::VertexAttribute::Vertex;
|
bool hasVertex = flags & BSVertexDesc::VertexAttribute::Vertex;
|
||||||
bool hasTangent = flags & BSVertexDesc::VertexAttribute::Tangents;
|
bool hasTangent = flags & BSVertexDesc::VertexAttribute::Tangents;
|
||||||
bool hasUV = flags & BSVertexDesc::VertexAttribute::UVs;
|
bool hasUV = flags & BSVertexDesc::VertexAttribute::UVs;
|
||||||
|
|
|
@ -119,14 +119,7 @@ namespace Nif
|
||||||
void post(Reader& nif) override;
|
void post(Reader& nif) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GeometryInterface
|
struct NiGeometry : NiAVObject
|
||||||
{
|
|
||||||
NiSkinInstancePtr mSkin;
|
|
||||||
BSShaderPropertyPtr mShaderProperty;
|
|
||||||
NiAlphaPropertyPtr mAlphaProperty;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NiGeometry : NiAVObject, GeometryInterface
|
|
||||||
{
|
{
|
||||||
/* Possible flags:
|
/* Possible flags:
|
||||||
0x40 - mesh has no vertex normals ?
|
0x40 - mesh has no vertex normals ?
|
||||||
|
@ -146,7 +139,10 @@ namespace Nif
|
||||||
};
|
};
|
||||||
|
|
||||||
NiGeometryDataPtr mData;
|
NiGeometryDataPtr mData;
|
||||||
|
NiSkinInstancePtr mSkin;
|
||||||
MaterialData mMaterial;
|
MaterialData mMaterial;
|
||||||
|
BSShaderPropertyPtr mShaderProperty;
|
||||||
|
NiAlphaPropertyPtr mAlphaProperty;
|
||||||
|
|
||||||
void read(NIFStream* nif) override;
|
void read(NIFStream* nif) override;
|
||||||
void post(Reader& nif) override;
|
void post(Reader& nif) override;
|
||||||
|
@ -365,10 +361,13 @@ namespace Nif
|
||||||
void read(NIFStream* nif, uint16_t flags);
|
void read(NIFStream* nif, uint16_t flags);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BSTriShape : NiAVObject, GeometryInterface
|
struct BSTriShape : NiAVObject
|
||||||
{
|
{
|
||||||
osg::BoundingSpheref mBoundingSphere;
|
osg::BoundingSpheref mBoundingSphere;
|
||||||
std::array<float, 6> mBoundMinMax;
|
std::array<float, 6> mBoundMinMax;
|
||||||
|
RecordPtrT<Record> mSkin;
|
||||||
|
BSShaderPropertyPtr mShaderProperty;
|
||||||
|
NiAlphaPropertyPtr mAlphaProperty;
|
||||||
BSVertexDesc mVertDesc;
|
BSVertexDesc mVertDesc;
|
||||||
uint32_t mDataSize;
|
uint32_t mDataSize;
|
||||||
std::vector<BSVertexData> mVertData;
|
std::vector<BSVertexData> mVertData;
|
||||||
|
|
|
@ -77,6 +77,8 @@ namespace Nif
|
||||||
RC_BSBound,
|
RC_BSBound,
|
||||||
RC_BSBoneLODExtraData,
|
RC_BSBoneLODExtraData,
|
||||||
RC_BSClothExtraData,
|
RC_BSClothExtraData,
|
||||||
|
RC_BSConnectPointChildren,
|
||||||
|
RC_BSConnectPointParents,
|
||||||
RC_BSDecalPlacementVectorExtraData,
|
RC_BSDecalPlacementVectorExtraData,
|
||||||
RC_BSDistantTreeShaderProperty,
|
RC_BSDistantTreeShaderProperty,
|
||||||
RC_BSDynamicTriShape,
|
RC_BSDynamicTriShape,
|
||||||
|
@ -87,6 +89,7 @@ namespace Nif
|
||||||
RC_BSEffectShaderPropertyColorController,
|
RC_BSEffectShaderPropertyColorController,
|
||||||
RC_BSEffectShaderPropertyFloatController,
|
RC_BSEffectShaderPropertyFloatController,
|
||||||
RC_BSExtraData,
|
RC_BSExtraData,
|
||||||
|
RC_BSEyeCenterExtraData,
|
||||||
RC_BSFrustumFOVController,
|
RC_BSFrustumFOVController,
|
||||||
RC_BSFurnitureMarker,
|
RC_BSFurnitureMarker,
|
||||||
RC_BSInvMarker,
|
RC_BSInvMarker,
|
||||||
|
@ -105,6 +108,7 @@ namespace Nif
|
||||||
RC_BSNiAlphaPropertyTestRefController,
|
RC_BSNiAlphaPropertyTestRefController,
|
||||||
RC_BSPackedAdditionalGeometryData,
|
RC_BSPackedAdditionalGeometryData,
|
||||||
RC_BSParentVelocityModifier,
|
RC_BSParentVelocityModifier,
|
||||||
|
RC_BSPositionData,
|
||||||
RC_BSProceduralLightningController,
|
RC_BSProceduralLightningController,
|
||||||
RC_BSPSysArrayEmitter,
|
RC_BSPSysArrayEmitter,
|
||||||
RC_BSPSysHavokUpdateModifier,
|
RC_BSPSysHavokUpdateModifier,
|
||||||
|
@ -125,6 +129,8 @@ namespace Nif
|
||||||
RC_BSShaderPPLightingProperty,
|
RC_BSShaderPPLightingProperty,
|
||||||
RC_BSShaderProperty,
|
RC_BSShaderProperty,
|
||||||
RC_BSShaderTextureSet,
|
RC_BSShaderTextureSet,
|
||||||
|
RC_BSSkinBoneData,
|
||||||
|
RC_BSSkinInstance,
|
||||||
RC_BSSkyShaderProperty,
|
RC_BSSkyShaderProperty,
|
||||||
RC_BSTriShape,
|
RC_BSTriShape,
|
||||||
RC_BSWArray,
|
RC_BSWArray,
|
||||||
|
@ -184,6 +190,7 @@ namespace Nif
|
||||||
RC_NiLight,
|
RC_NiLight,
|
||||||
RC_NiLightColorController,
|
RC_NiLightColorController,
|
||||||
RC_NiLightDimmerController,
|
RC_NiLightDimmerController,
|
||||||
|
RC_NiLightRadiusController,
|
||||||
RC_NiLines,
|
RC_NiLines,
|
||||||
RC_NiLinesData,
|
RC_NiLinesData,
|
||||||
RC_NiLODNode,
|
RC_NiLODNode,
|
||||||
|
|
|
@ -162,6 +162,7 @@ namespace Nif
|
||||||
struct bhkCompressedMeshShapeData;
|
struct bhkCompressedMeshShapeData;
|
||||||
struct BSMultiBound;
|
struct BSMultiBound;
|
||||||
struct BSMultiBoundData;
|
struct BSMultiBoundData;
|
||||||
|
struct BSSkinBoneData;
|
||||||
|
|
||||||
using NiAVObjectPtr = RecordPtrT<NiAVObject>;
|
using NiAVObjectPtr = RecordPtrT<NiAVObject>;
|
||||||
using ExtraPtr = RecordPtrT<Extra>;
|
using ExtraPtr = RecordPtrT<Extra>;
|
||||||
|
@ -211,6 +212,7 @@ namespace Nif
|
||||||
using bhkCompressedMeshShapeDataPtr = RecordPtrT<bhkCompressedMeshShapeData>;
|
using bhkCompressedMeshShapeDataPtr = RecordPtrT<bhkCompressedMeshShapeData>;
|
||||||
using BSMultiBoundPtr = RecordPtrT<BSMultiBound>;
|
using BSMultiBoundPtr = RecordPtrT<BSMultiBound>;
|
||||||
using BSMultiBoundDataPtr = RecordPtrT<BSMultiBoundData>;
|
using BSMultiBoundDataPtr = RecordPtrT<BSMultiBoundData>;
|
||||||
|
using BSSkinBoneDataPtr = RecordPtrT<BSSkinBoneData>;
|
||||||
|
|
||||||
using NiAVObjectList = RecordListT<NiAVObject>;
|
using NiAVObjectList = RecordListT<NiAVObject>;
|
||||||
using NiPropertyList = RecordListT<NiProperty>;
|
using NiPropertyList = RecordListT<NiProperty>;
|
||||||
|
|
|
@ -32,6 +32,32 @@ namespace
|
||||||
return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X');
|
return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isTypeNiGeometry(int type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case Nif::RC_NiTriShape:
|
||||||
|
case Nif::RC_NiTriStrips:
|
||||||
|
case Nif::RC_BSLODTriShape:
|
||||||
|
case Nif::RC_BSSegmentedTriShape:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTypeTriShape(int type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case Nif::RC_NiTriShape:
|
||||||
|
case Nif::RC_BSLODTriShape:
|
||||||
|
case Nif::RC_BSSegmentedTriShape:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void prepareTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data)
|
void prepareTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data)
|
||||||
{
|
{
|
||||||
// FIXME: copying vertices/indices individually is unreasonable
|
// FIXME: copying vertices/indices individually is unreasonable
|
||||||
|
@ -81,7 +107,7 @@ namespace
|
||||||
auto handleNiGeometry(const Nif::NiGeometry& geometry, Function&& function)
|
auto handleNiGeometry(const Nif::NiGeometry& geometry, Function&& function)
|
||||||
-> decltype(function(static_cast<const Nif::NiTriShapeData&>(geometry.mData.get())))
|
-> decltype(function(static_cast<const Nif::NiTriShapeData&>(geometry.mData.get())))
|
||||||
{
|
{
|
||||||
if (geometry.recType == Nif::RC_NiTriShape || geometry.recType == Nif::RC_BSLODTriShape)
|
if (isTypeTriShape(geometry.recType))
|
||||||
{
|
{
|
||||||
auto data = static_cast<const Nif::NiTriShapeData*>(geometry.mData.getPtr());
|
auto data = static_cast<const Nif::NiTriShapeData*>(geometry.mData.getPtr());
|
||||||
if (data->mTriangles.empty())
|
if (data->mTriangles.empty())
|
||||||
|
@ -329,12 +355,8 @@ namespace NifBullet
|
||||||
// NOTE: a trishape with bounds, but no BBoxCollision flag should NOT go through handleNiTriShape!
|
// NOTE: a trishape with bounds, but no BBoxCollision flag should NOT go through handleNiTriShape!
|
||||||
// It must be ignored completely.
|
// It must be ignored completely.
|
||||||
// (occurs in tr_ex_imp_wall_arch_04.nif)
|
// (occurs in tr_ex_imp_wall_arch_04.nif)
|
||||||
if (node.mBounds.mType == Nif::BoundingVolume::Type::BASE_BV
|
if (node.mBounds.mType == Nif::BoundingVolume::Type::BASE_BV && isTypeNiGeometry(node.recType))
|
||||||
&& (node.recType == Nif::RC_NiTriShape || node.recType == Nif::RC_NiTriStrips
|
|
||||||
|| node.recType == Nif::RC_BSLODTriShape))
|
|
||||||
{
|
|
||||||
handleNiTriShape(static_cast<const Nif::NiGeometry&>(node), parent, args);
|
handleNiTriShape(static_cast<const Nif::NiGeometry&>(node), parent, args);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For NiNodes, loop through children
|
// For NiNodes, loop through children
|
||||||
|
|
|
@ -88,7 +88,7 @@ namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isTypeGeometry(int type)
|
bool isTypeNiGeometry(int type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
@ -96,6 +96,19 @@ namespace
|
||||||
case Nif::RC_NiTriStrips:
|
case Nif::RC_NiTriStrips:
|
||||||
case Nif::RC_NiLines:
|
case Nif::RC_NiLines:
|
||||||
case Nif::RC_BSLODTriShape:
|
case Nif::RC_BSLODTriShape:
|
||||||
|
case Nif::RC_BSSegmentedTriShape:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTypeBSGeometry(int type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case Nif::RC_BSTriShape:
|
||||||
|
case Nif::RC_BSDynamicTriShape:
|
||||||
|
case Nif::RC_BSMeshLODTriShape:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -125,15 +138,6 @@ namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto geometry = dynamic_cast<const Nif::NiGeometry*>(nifNode);
|
|
||||||
if (geometry)
|
|
||||||
{
|
|
||||||
if (!geometry->mShaderProperty.empty())
|
|
||||||
out.emplace_back(geometry->mShaderProperty.getPtr());
|
|
||||||
if (!geometry->mAlphaProperty.empty())
|
|
||||||
out.emplace_back(geometry->mAlphaProperty.getPtr());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeCallback used to have a node always oriented towards the camera. The node can have translation and scale
|
// NodeCallback used to have a node always oriented towards the camera. The node can have translation and scale
|
||||||
|
@ -443,11 +447,16 @@ namespace NifOsg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto geometry = dynamic_cast<const Nif::NiGeometry*>(nifNode);
|
// NiAlphaProperty is handled as a drawable property
|
||||||
// NiGeometry's NiAlphaProperty doesn't get handled here because it's a drawable property
|
Nif::BSShaderPropertyPtr shaderprop = nullptr;
|
||||||
if (geometry && !geometry->mShaderProperty.empty())
|
if (isTypeNiGeometry(nifNode->recType))
|
||||||
handleProperty(geometry->mShaderProperty.getPtr(), applyTo, composite, imageManager, boundTextures,
|
shaderprop = static_cast<const Nif::NiGeometry*>(nifNode)->mShaderProperty;
|
||||||
animflags, hasStencilProperty);
|
else if (isTypeBSGeometry(nifNode->recType))
|
||||||
|
shaderprop = static_cast<const Nif::BSTriShape*>(nifNode)->mShaderProperty;
|
||||||
|
|
||||||
|
if (!shaderprop.empty())
|
||||||
|
handleProperty(shaderprop.getPtr(), applyTo, composite, imageManager, boundTextures, animflags,
|
||||||
|
hasStencilProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setupController(const Nif::NiTimeController* ctrl, SceneUtil::Controller* toSetup, int animflags)
|
static void setupController(const Nif::NiTimeController* ctrl, SceneUtil::Controller* toSetup, int animflags)
|
||||||
|
@ -747,7 +756,9 @@ namespace NifOsg
|
||||||
|
|
||||||
applyNodeProperties(nifNode, node, composite, args.mImageManager, args.mBoundTextures, args.mAnimFlags);
|
applyNodeProperties(nifNode, node, composite, args.mImageManager, args.mBoundTextures, args.mAnimFlags);
|
||||||
|
|
||||||
const bool isGeometry = isTypeGeometry(nifNode->recType);
|
const bool isNiGeometry = isTypeNiGeometry(nifNode->recType);
|
||||||
|
const bool isBSGeometry = isTypeBSGeometry(nifNode->recType);
|
||||||
|
const bool isGeometry = isNiGeometry || isBSGeometry;
|
||||||
|
|
||||||
if (isGeometry && !args.mSkipMeshes)
|
if (isGeometry && !args.mSkipMeshes)
|
||||||
{
|
{
|
||||||
|
@ -762,12 +773,10 @@ namespace NifOsg
|
||||||
skip = args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->mName, "EditorMarker");
|
skip = args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->mName, "EditorMarker");
|
||||||
if (!skip)
|
if (!skip)
|
||||||
{
|
{
|
||||||
Nif::NiSkinInstancePtr skin = static_cast<const Nif::NiGeometry*>(nifNode)->mSkin;
|
if (isNiGeometry)
|
||||||
|
handleNiGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags);
|
||||||
if (skin.empty())
|
else // isBSGeometry
|
||||||
handleGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags);
|
handleBSGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags);
|
||||||
else
|
|
||||||
handleSkinnedGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags);
|
|
||||||
|
|
||||||
if (!nifNode->mController.empty())
|
if (!nifNode->mController.empty())
|
||||||
handleMeshControllers(nifNode, node, composite, args.mBoundTextures, args.mAnimFlags);
|
handleMeshControllers(nifNode, node, composite, args.mBoundTextures, args.mAnimFlags);
|
||||||
|
@ -1342,41 +1351,7 @@ namespace NifOsg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleNiGeometryData(osg::Geometry* geometry, const Nif::NiGeometryData* data,
|
void handleNiGeometryData(const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Geometry* geometry,
|
||||||
const std::vector<unsigned int>& boundTextures, const std::string& name)
|
|
||||||
{
|
|
||||||
const auto& vertices = data->mVertices;
|
|
||||||
const auto& normals = data->mNormals;
|
|
||||||
const auto& colors = data->mColors;
|
|
||||||
if (!vertices.empty())
|
|
||||||
geometry->setVertexArray(new osg::Vec3Array(vertices.size(), vertices.data()));
|
|
||||||
if (!normals.empty())
|
|
||||||
geometry->setNormalArray(
|
|
||||||
new osg::Vec3Array(normals.size(), normals.data()), osg::Array::BIND_PER_VERTEX);
|
|
||||||
if (!colors.empty())
|
|
||||||
geometry->setColorArray(new osg::Vec4Array(colors.size(), colors.data()), osg::Array::BIND_PER_VERTEX);
|
|
||||||
|
|
||||||
const auto& uvlist = data->mUVList;
|
|
||||||
int textureStage = 0;
|
|
||||||
for (std::vector<unsigned int>::const_iterator it = boundTextures.begin(); it != boundTextures.end();
|
|
||||||
++it, ++textureStage)
|
|
||||||
{
|
|
||||||
unsigned int uvSet = *it;
|
|
||||||
if (uvSet >= uvlist.size())
|
|
||||||
{
|
|
||||||
Log(Debug::Verbose) << "Out of bounds UV set " << uvSet << " on shape \"" << name << "\" in "
|
|
||||||
<< mFilename;
|
|
||||||
if (uvlist.empty())
|
|
||||||
continue;
|
|
||||||
uvSet = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
geometry->setTexCoordArray(textureStage, new osg::Vec2Array(uvlist[uvSet].size(), uvlist[uvSet].data()),
|
|
||||||
osg::Array::BIND_PER_VERTEX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleNiGeometry(const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Geometry* geometry,
|
|
||||||
osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite,
|
osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite,
|
||||||
const std::vector<unsigned int>& boundTextures, int animflags)
|
const std::vector<unsigned int>& boundTextures, int animflags)
|
||||||
{
|
{
|
||||||
|
@ -1458,7 +1433,36 @@ namespace NifOsg
|
||||||
new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, line.size(), line.data()));
|
new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, line.size(), line.data()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleNiGeometryData(geometry, niGeometryData, boundTextures, nifNode->mName);
|
|
||||||
|
const auto& vertices = niGeometryData->mVertices;
|
||||||
|
const auto& normals = niGeometryData->mNormals;
|
||||||
|
const auto& colors = niGeometryData->mColors;
|
||||||
|
if (!vertices.empty())
|
||||||
|
geometry->setVertexArray(new osg::Vec3Array(vertices.size(), vertices.data()));
|
||||||
|
if (!normals.empty())
|
||||||
|
geometry->setNormalArray(
|
||||||
|
new osg::Vec3Array(normals.size(), normals.data()), osg::Array::BIND_PER_VERTEX);
|
||||||
|
if (!colors.empty())
|
||||||
|
geometry->setColorArray(new osg::Vec4Array(colors.size(), colors.data()), osg::Array::BIND_PER_VERTEX);
|
||||||
|
|
||||||
|
const auto& uvlist = niGeometryData->mUVList;
|
||||||
|
int textureStage = 0;
|
||||||
|
for (std::vector<unsigned int>::const_iterator it = boundTextures.begin(); it != boundTextures.end();
|
||||||
|
++it, ++textureStage)
|
||||||
|
{
|
||||||
|
unsigned int uvSet = *it;
|
||||||
|
if (uvSet >= uvlist.size())
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << "Out of bounds UV set " << uvSet << " on shape \"" << nifNode->mName
|
||||||
|
<< "\" in " << mFilename;
|
||||||
|
if (uvlist.empty())
|
||||||
|
continue;
|
||||||
|
uvSet = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
geometry->setTexCoordArray(textureStage, new osg::Vec2Array(uvlist[uvSet].size(), uvlist[uvSet].data()),
|
||||||
|
osg::Array::BIND_PER_VERTEX);
|
||||||
|
}
|
||||||
|
|
||||||
// osg::Material properties are handled here for two reasons:
|
// osg::Material properties are handled here for two reasons:
|
||||||
// - if there are no vertex colors, we need to disable colorMode.
|
// - if there are no vertex colors, we need to disable colorMode.
|
||||||
|
@ -1466,98 +1470,226 @@ namespace NifOsg
|
||||||
// above the actual renderable would be tedious.
|
// above the actual renderable would be tedious.
|
||||||
std::vector<const Nif::NiProperty*> drawableProps;
|
std::vector<const Nif::NiProperty*> drawableProps;
|
||||||
collectDrawableProperties(nifNode, parent, drawableProps);
|
collectDrawableProperties(nifNode, parent, drawableProps);
|
||||||
|
if (!niGeometry->mShaderProperty.empty())
|
||||||
|
drawableProps.emplace_back(niGeometry->mShaderProperty.getPtr());
|
||||||
|
if (!niGeometry->mAlphaProperty.empty())
|
||||||
|
drawableProps.emplace_back(niGeometry->mAlphaProperty.getPtr());
|
||||||
applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->mColors.empty(), animflags);
|
applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->mColors.empty(), animflags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleGeometry(const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
|
void handleNiGeometry(const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
|
||||||
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures,
|
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures,
|
||||||
int animflags)
|
int animflags)
|
||||||
{
|
{
|
||||||
assert(isTypeGeometry(nifNode->recType));
|
assert(isTypeNiGeometry(nifNode->recType));
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geometry> geom(new osg::Geometry);
|
osg::ref_ptr<osg::Geometry> geom(new osg::Geometry);
|
||||||
handleNiGeometry(nifNode, parent, geom, parentNode, composite, boundTextures, animflags);
|
handleNiGeometryData(nifNode, parent, geom, parentNode, composite, boundTextures, animflags);
|
||||||
// If the record had no valid geometry data in it, early-out
|
// If the record had no valid geometry data in it, early-out
|
||||||
if (geom->empty())
|
if (geom->empty())
|
||||||
return;
|
return;
|
||||||
osg::ref_ptr<osg::Drawable> drawable;
|
|
||||||
|
osg::ref_ptr<osg::Drawable> drawable = geom;
|
||||||
|
|
||||||
|
auto niGeometry = static_cast<const Nif::NiGeometry*>(nifNode);
|
||||||
|
if (!niGeometry->mSkin.empty())
|
||||||
|
{
|
||||||
|
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
|
||||||
|
rig->setSourceGeometry(geom);
|
||||||
|
|
||||||
|
// Assign bone weights
|
||||||
|
osg::ref_ptr<SceneUtil::RigGeometry::InfluenceMap> map(new SceneUtil::RigGeometry::InfluenceMap);
|
||||||
|
|
||||||
|
const Nif::NiSkinInstance* skin = niGeometry->mSkin.getPtr();
|
||||||
|
const Nif::NiSkinData* data = skin->mData.getPtr();
|
||||||
|
const Nif::NiAVObjectList& bones = skin->mBones;
|
||||||
|
for (std::size_t i = 0; i < bones.size(); ++i)
|
||||||
|
{
|
||||||
|
std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->mName);
|
||||||
|
|
||||||
|
SceneUtil::RigGeometry::BoneInfluence influence;
|
||||||
|
influence.mWeights = data->mBones[i].mWeights;
|
||||||
|
influence.mInvBindMatrix = data->mBones[i].mTransform.toMatrix();
|
||||||
|
influence.mBoundSphere = data->mBones[i].mBoundSphere;
|
||||||
|
|
||||||
|
map->mData.emplace_back(boneName, influence);
|
||||||
|
}
|
||||||
|
rig->setInfluenceMap(map);
|
||||||
|
|
||||||
|
drawable = rig;
|
||||||
|
}
|
||||||
|
|
||||||
for (Nif::NiTimeControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->mNext)
|
for (Nif::NiTimeControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->mNext)
|
||||||
{
|
{
|
||||||
if (!ctrl->isActive())
|
if (!ctrl->isActive())
|
||||||
continue;
|
continue;
|
||||||
if (ctrl->recType == Nif::RC_NiGeomMorpherController)
|
if (ctrl->recType == Nif::RC_NiGeomMorpherController)
|
||||||
{
|
{
|
||||||
const Nif::NiGeomMorpherController* nimorphctrl
|
if (!niGeometry->mSkin.empty())
|
||||||
= static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
|
continue;
|
||||||
|
|
||||||
|
auto nimorphctrl = static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
|
||||||
if (nimorphctrl->mData.empty())
|
if (nimorphctrl->mData.empty())
|
||||||
continue;
|
continue;
|
||||||
drawable = handleMorphGeometry(nimorphctrl, geom, parentNode, composite, boundTextures, animflags);
|
|
||||||
|
const std::vector<Nif::NiMorphData::MorphData>& morphs = nimorphctrl->mData.getPtr()->mMorphs;
|
||||||
|
if (morphs.empty()
|
||||||
|
|| morphs[0].mVertices.size()
|
||||||
|
!= static_cast<const osg::Vec3Array*>(geom->getVertexArray())->size())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
osg::ref_ptr<SceneUtil::MorphGeometry> morphGeom = new SceneUtil::MorphGeometry;
|
||||||
|
morphGeom->setSourceGeometry(geom);
|
||||||
|
for (unsigned int i = 0; i < morphs.size(); ++i)
|
||||||
|
morphGeom->addMorphTarget(
|
||||||
|
new osg::Vec3Array(morphs[i].mVertices.size(), morphs[i].mVertices.data()), 0.f);
|
||||||
|
|
||||||
osg::ref_ptr<GeomMorpherController> morphctrl = new GeomMorpherController(nimorphctrl);
|
osg::ref_ptr<GeomMorpherController> morphctrl = new GeomMorpherController(nimorphctrl);
|
||||||
setupController(ctrl.getPtr(), morphctrl, animflags);
|
setupController(ctrl.getPtr(), morphctrl, animflags);
|
||||||
drawable->setUpdateCallback(morphctrl);
|
morphGeom->setUpdateCallback(morphctrl);
|
||||||
|
|
||||||
|
drawable = morphGeom;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!drawable.get())
|
|
||||||
drawable = geom;
|
|
||||||
drawable->setName(nifNode->mName);
|
drawable->setName(nifNode->mName);
|
||||||
parentNode->addChild(drawable);
|
parentNode->addChild(drawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Drawable> handleMorphGeometry(const Nif::NiGeomMorpherController* morpher,
|
void handleBSGeometry(const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
|
||||||
osg::ref_ptr<osg::Geometry> sourceGeometry, osg::Node* parentNode,
|
|
||||||
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures,
|
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures,
|
||||||
int animflags)
|
int animflags)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<SceneUtil::MorphGeometry> morphGeom = new SceneUtil::MorphGeometry;
|
assert(isTypeBSGeometry(nifNode->recType));
|
||||||
morphGeom->setSourceGeometry(sourceGeometry);
|
|
||||||
|
|
||||||
const std::vector<Nif::NiMorphData::MorphData>& morphs = morpher->mData.getPtr()->mMorphs;
|
auto bsTriShape = static_cast<const Nif::BSTriShape*>(nifNode);
|
||||||
if (morphs.empty())
|
const std::vector<unsigned short>& triangles = bsTriShape->mTriangles;
|
||||||
return morphGeom;
|
if (triangles.empty())
|
||||||
if (morphs[0].mVertices.size()
|
|
||||||
!= static_cast<const osg::Vec3Array*>(sourceGeometry->getVertexArray())->size())
|
|
||||||
return morphGeom;
|
|
||||||
for (unsigned int i = 0; i < morphs.size(); ++i)
|
|
||||||
morphGeom->addMorphTarget(
|
|
||||||
new osg::Vec3Array(morphs[i].mVertices.size(), morphs[i].mVertices.data()), 0.f);
|
|
||||||
|
|
||||||
return morphGeom;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleSkinnedGeometry(const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
|
|
||||||
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures,
|
|
||||||
int animflags)
|
|
||||||
{
|
|
||||||
assert(isTypeGeometry(nifNode->recType));
|
|
||||||
osg::ref_ptr<osg::Geometry> geometry(new osg::Geometry);
|
|
||||||
handleNiGeometry(nifNode, parent, geometry, parentNode, composite, boundTextures, animflags);
|
|
||||||
if (geometry->empty())
|
|
||||||
return;
|
return;
|
||||||
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
|
|
||||||
rig->setSourceGeometry(geometry);
|
|
||||||
rig->setName(nifNode->mName);
|
|
||||||
|
|
||||||
// Assign bone weights
|
osg::ref_ptr<osg::Geometry> geometry(new osg::Geometry);
|
||||||
osg::ref_ptr<SceneUtil::RigGeometry::InfluenceMap> map(new SceneUtil::RigGeometry::InfluenceMap);
|
geometry->addPrimitiveSet(
|
||||||
|
new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, triangles.size(), triangles.data()));
|
||||||
|
|
||||||
const Nif::NiSkinInstance* skin = static_cast<const Nif::NiGeometry*>(nifNode)->mSkin.getPtr();
|
osg::ref_ptr<osg::Drawable> drawable = geometry;
|
||||||
const Nif::NiSkinData* data = skin->mData.getPtr();
|
|
||||||
const Nif::NiAVObjectList& bones = skin->mBones;
|
// Some input geometry may not be used as is so it needs to be converted.
|
||||||
for (std::size_t i = 0; i < bones.size(); ++i)
|
// Normals, tangents and bitangents use a special normal map-like format not equivalent to snorm8 or unorm8
|
||||||
|
auto normbyteToFloat = [](uint8_t value) { return value / 255.f * 2.f - 1.f; };
|
||||||
|
// Vertices and UV sets may be half-precision.
|
||||||
|
// OSG doesn't have a way to pass half-precision data at the moment.
|
||||||
|
auto halfToFloat = [](uint16_t value) {
|
||||||
|
uint32_t bits = static_cast<uint32_t>(value & 0x8000) << 16;
|
||||||
|
|
||||||
|
const uint32_t exp16 = (value & 0x7c00) >> 10;
|
||||||
|
uint32_t frac16 = value & 0x3ff;
|
||||||
|
if (exp16)
|
||||||
|
bits |= (exp16 + 0x70) << 23;
|
||||||
|
else if (frac16)
|
||||||
|
{
|
||||||
|
uint8_t offset = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
++offset;
|
||||||
|
frac16 <<= 1;
|
||||||
|
} while ((frac16 & 0x400) != 0x400);
|
||||||
|
frac16 &= 0x3ff;
|
||||||
|
bits |= (0x71 - offset) << 23;
|
||||||
|
}
|
||||||
|
bits |= frac16 << 13;
|
||||||
|
|
||||||
|
float result;
|
||||||
|
std::memcpy(&result, &bits, sizeof(float));
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool fullPrec = bsTriShape->mVertDesc.mFlags & Nif::BSVertexDesc::VertexAttribute::Full_Precision;
|
||||||
|
const bool hasVertices = bsTriShape->mVertDesc.mFlags & Nif::BSVertexDesc::VertexAttribute::Vertex;
|
||||||
|
const bool hasNormals = bsTriShape->mVertDesc.mFlags & Nif::BSVertexDesc::VertexAttribute::Normals;
|
||||||
|
const bool hasColors = bsTriShape->mVertDesc.mFlags & Nif::BSVertexDesc::VertexAttribute::Vertex_Colors;
|
||||||
|
const bool hasUV = bsTriShape->mVertDesc.mFlags & Nif::BSVertexDesc::VertexAttribute::UVs;
|
||||||
|
|
||||||
|
std::vector<osg::Vec3f> vertices;
|
||||||
|
std::vector<osg::Vec3f> normals;
|
||||||
|
std::vector<osg::Vec4ub> colors;
|
||||||
|
std::vector<osg::Vec2f> uvlist;
|
||||||
|
for (auto& elem : bsTriShape->mVertData)
|
||||||
{
|
{
|
||||||
std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->mName);
|
if (hasVertices)
|
||||||
|
{
|
||||||
SceneUtil::RigGeometry::BoneInfluence influence;
|
if (fullPrec)
|
||||||
influence.mWeights = data->mBones[i].mWeights;
|
vertices.emplace_back(elem.mVertex.x(), elem.mVertex.y(), elem.mVertex.z());
|
||||||
influence.mInvBindMatrix = data->mBones[i].mTransform.toMatrix();
|
else
|
||||||
influence.mBoundSphere = data->mBones[i].mBoundSphere;
|
vertices.emplace_back(halfToFloat(elem.mHalfVertex[0]), halfToFloat(elem.mHalfVertex[1]),
|
||||||
|
halfToFloat(elem.mHalfVertex[2]));
|
||||||
map->mData.emplace_back(boneName, influence);
|
}
|
||||||
|
if (hasNormals)
|
||||||
|
normals.emplace_back(normbyteToFloat(elem.mNormal[0]), normbyteToFloat(elem.mNormal[1]),
|
||||||
|
normbyteToFloat(elem.mNormal[2]));
|
||||||
|
if (hasColors)
|
||||||
|
colors.emplace_back(elem.mVertColor[0], elem.mVertColor[1], elem.mVertColor[2], elem.mVertColor[3]);
|
||||||
|
if (hasUV)
|
||||||
|
uvlist.emplace_back(halfToFloat(elem.mUV[0]), 1.0 - halfToFloat(elem.mUV[1]));
|
||||||
}
|
}
|
||||||
rig->setInfluenceMap(map);
|
|
||||||
|
|
||||||
parentNode->addChild(rig);
|
if (!vertices.empty())
|
||||||
|
geometry->setVertexArray(new osg::Vec3Array(vertices.size(), vertices.data()));
|
||||||
|
if (!normals.empty())
|
||||||
|
geometry->setNormalArray(
|
||||||
|
new osg::Vec3Array(normals.size(), normals.data()), osg::Array::BIND_PER_VERTEX);
|
||||||
|
if (!colors.empty())
|
||||||
|
geometry->setColorArray(
|
||||||
|
new osg::Vec4ubArray(colors.size(), colors.data()), osg::Array::BIND_PER_VERTEX);
|
||||||
|
if (!uvlist.empty())
|
||||||
|
geometry->setTexCoordArray(
|
||||||
|
0, new osg::Vec2Array(uvlist.size(), uvlist.data()), osg::Array::BIND_PER_VERTEX);
|
||||||
|
|
||||||
|
// This is the skinning data Fallout 4 provides
|
||||||
|
// TODO: support Skyrim SE skinning data
|
||||||
|
if (!bsTriShape->mSkin.empty() && bsTriShape->mSkin->recType == Nif::RC_BSSkinInstance
|
||||||
|
&& bsTriShape->mVertDesc.mFlags & Nif::BSVertexDesc::VertexAttribute::Skinned)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
|
||||||
|
rig->setSourceGeometry(geometry);
|
||||||
|
|
||||||
|
osg::ref_ptr<SceneUtil::RigGeometry::InfluenceMap> map(new SceneUtil::RigGeometry::InfluenceMap);
|
||||||
|
|
||||||
|
auto skin = static_cast<const Nif::BSSkinInstance*>(bsTriShape->mSkin.getPtr());
|
||||||
|
const Nif::BSSkinBoneData* data = skin->mData.getPtr();
|
||||||
|
const Nif::NiAVObjectList& bones = skin->mBones;
|
||||||
|
std::vector<std::vector<Nif::NiSkinData::VertWeight>> vertWeights(data->mBones.size());
|
||||||
|
for (size_t i = 0; i < vertices.size(); i++)
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
vertWeights[bsTriShape->mVertData[i].mBoneIndices[j]].emplace_back(
|
||||||
|
i, halfToFloat(bsTriShape->mVertData[i].mBoneWeights[j]));
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < bones.size(); ++i)
|
||||||
|
{
|
||||||
|
std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->mName);
|
||||||
|
|
||||||
|
SceneUtil::RigGeometry::BoneInfluence influence;
|
||||||
|
influence.mWeights = vertWeights[i];
|
||||||
|
influence.mInvBindMatrix = data->mBones[i].mTransform.toMatrix();
|
||||||
|
influence.mBoundSphere = data->mBones[i].mBoundSphere;
|
||||||
|
|
||||||
|
map->mData.emplace_back(boneName, influence);
|
||||||
|
}
|
||||||
|
rig->setInfluenceMap(map);
|
||||||
|
|
||||||
|
drawable = rig;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const Nif::NiProperty*> drawableProps;
|
||||||
|
collectDrawableProperties(nifNode, parent, drawableProps);
|
||||||
|
if (!bsTriShape->mShaderProperty.empty())
|
||||||
|
drawableProps.emplace_back(bsTriShape->mShaderProperty.getPtr());
|
||||||
|
if (!bsTriShape->mAlphaProperty.empty())
|
||||||
|
drawableProps.emplace_back(bsTriShape->mAlphaProperty.getPtr());
|
||||||
|
applyDrawableProperties(parentNode, drawableProps, composite, !colors.empty(), animflags);
|
||||||
|
|
||||||
|
drawable->setName(nifNode->mName);
|
||||||
|
parentNode->addChild(drawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::BlendFunc::BlendFuncMode getBlendMode(int mode)
|
osg::BlendFunc::BlendFuncMode getBlendMode(int mode)
|
||||||
|
|
Loading…
Reference in a new issue