From 2f4e5664589a15539c2f0e95e9207081533e6785 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 30 Nov 2024 13:23:47 +0100 Subject: [PATCH] Take skin transform and skeleton root into account --- components/nifosg/nifloader.cpp | 3 + components/sceneutil/riggeometry.cpp | 88 +++++++++++++++++++--------- components/sceneutil/riggeometry.hpp | 12 +++- 3 files changed, 72 insertions(+), 31 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index d817ed2c9f..9451017f60 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1567,6 +1567,9 @@ namespace NifOsg } rig->setBoneInfo(std::move(boneInfo)); rig->setInfluences(influences); + rig->setTransform(data->mTransform.toMatrix()); + if (const Nif::NiAVObject* rootBone = skin->mRoot.getPtr()) + rig->setRootBone(rootBone->mName); drawable = rig; } diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index d93b88349d..8eb22895cf 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include "skeleton.hpp" @@ -181,6 +182,12 @@ namespace SceneUtil ++boneInfo; } + osg::Matrixf transform; + if (mSkinToSkelMatrix) + transform = (*mSkinToSkelMatrix) * mData->mTransform; + else + transform = mData->mTransform; + for (const auto& [influences, vertices] : mData->mInfluences) { osg::Matrixf resultMat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); @@ -196,8 +203,7 @@ namespace SceneUtil *resultMatPtr += *boneMatPtr * weight; } - if (mGeomToSkelMatrix) - resultMat *= (*mGeomToSkelMatrix); + resultMat *= transform; for (unsigned short vertex : vertices) { @@ -242,9 +248,14 @@ namespace SceneUtil mSkeleton->updateBoneMatrices(nv->getTraversalNumber()); - updateGeomToSkelMatrix(nv->getNodePath()); + updateSkinToSkelMatrix(nv->getNodePath()); osg::BoundingBox box; + osg::Matrixf transform; + if (mSkinToSkelMatrix) + transform = (*mSkinToSkelMatrix) * mData->mTransform; + else + transform = mData->mTransform; size_t index = 0; for (const BoneInfo& info : mData->mBones) @@ -254,10 +265,7 @@ namespace SceneUtil continue; osg::BoundingSpheref bs = info.mBoundSphere; - if (mGeomToSkelMatrix) - transformBoundingSphere(bone->mMatrixInSkeletonSpace * (*mGeomToSkelMatrix), bs); - else - transformBoundingSphere(bone->mMatrixInSkeletonSpace, bs); + transformBoundingSphere(bone->mMatrixInSkeletonSpace * transform, bs); box.expandBy(bs); } @@ -280,31 +288,39 @@ namespace SceneUtil } } - void RigGeometry::updateGeomToSkelMatrix(const osg::NodePath& nodePath) + void RigGeometry::updateSkinToSkelMatrix(const osg::NodePath& nodePath) { - bool foundSkel = false; - osg::RefMatrix* geomToSkelMatrix = mGeomToSkelMatrix; - if (geomToSkelMatrix) - geomToSkelMatrix->makeIdentity(); - for (osg::NodePath::const_iterator it = nodePath.begin(); it != nodePath.end() - 1; ++it) + if (mSkinToSkelMatrix) + mSkinToSkelMatrix->makeIdentity(); + auto skeletonRoot = std::find(nodePath.begin(), nodePath.end(), mSkeleton); + if (skeletonRoot == nodePath.end()) + return; + skeletonRoot++; + auto skinRoot = nodePath.end(); + if (!mData->mRootBone.empty()) + skinRoot = std::find_if(skeletonRoot, nodePath.end(), + [&](const osg::Node* node) { return Misc::StringUtils::ciEqual(node->getName(), mData->mRootBone); }); + if (skinRoot == nodePath.end()) { - osg::Node* node = *it; - if (!foundSkel) + // Failed to find skin root, cancel out everything up till the trishape. + // Our parent node is the trishape's transform + skinRoot = nodePath.end() - 2; + if ((*skinRoot)->getName() != getName()) // but maybe it can get optimized out + skinRoot++; + } + else + skinRoot++; + for (auto it = skeletonRoot; it != skinRoot; ++it) + { + const osg::Node* node = *it; + if (const osg::Transform* trans = node->asTransform()) { - if (node == mSkeleton) - foundSkel = true; - } - else - { - if (osg::Transform* trans = node->asTransform()) - { - osg::MatrixTransform* matrixTrans = trans->asMatrixTransform(); - if (matrixTrans && matrixTrans->getMatrix().isIdentity()) - continue; - if (!geomToSkelMatrix) - geomToSkelMatrix = mGeomToSkelMatrix = new osg::RefMatrix; - trans->computeWorldToLocalMatrix(*geomToSkelMatrix, nullptr); - } + const osg::MatrixTransform* matrixTrans = trans->asMatrixTransform(); + if (matrixTrans && matrixTrans->getMatrix().isIdentity()) + continue; + if (!mSkinToSkelMatrix) + mSkinToSkelMatrix = new osg::RefMatrix; + trans->computeWorldToLocalMatrix(*mSkinToSkelMatrix, nullptr); } } } @@ -352,6 +368,20 @@ namespace SceneUtil mData->mInfluences.assign(influencesToVertices.begin(), influencesToVertices.end()); } + void RigGeometry::setTransform(osg::Matrixf&& transform) + { + if (!mData) + mData = new InfluenceData; + mData->mTransform = transform; + } + + void RigGeometry::setRootBone(std::string_view name) + { + if (!mData) + mData = new InfluenceData; + mData->mRootBone = name; + } + void RigGeometry::accept(osg::NodeVisitor& nv) { if (!nv.validNodeMask(*this)) diff --git a/components/sceneutil/riggeometry.hpp b/components/sceneutil/riggeometry.hpp index 64ea1e2519..670c040758 100644 --- a/components/sceneutil/riggeometry.hpp +++ b/components/sceneutil/riggeometry.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace SceneUtil { class Skeleton; @@ -58,6 +60,10 @@ namespace SceneUtil /// @note The source geometry will not be modified. void setSourceGeometry(osg::ref_ptr sourceGeom); + void setTransform(osg::Matrixf&& transform); + + void setRootBone(std::string_view name); + osg::ref_ptr getSourceGeometry() const; void accept(osg::NodeVisitor& nv) override; @@ -89,13 +95,15 @@ namespace SceneUtil osg::ref_ptr mSourceTangents; Skeleton* mSkeleton{ nullptr }; - osg::ref_ptr mGeomToSkelMatrix; + osg::ref_ptr mSkinToSkelMatrix; using VertexList = std::vector; struct InfluenceData : public osg::Referenced { std::vector mBones; std::vector> mInfluences; + osg::Matrixf mTransform; + std::string mRootBone; }; osg::ref_ptr mData; std::vector mNodes; @@ -105,7 +113,7 @@ namespace SceneUtil bool initFromParentSkeleton(osg::NodeVisitor* nv); - void updateGeomToSkelMatrix(const osg::NodePath& nodePath); + void updateSkinToSkelMatrix(const osg::NodePath& nodePath); }; }