Take skin transform and skeleton root into account

This commit is contained in:
Evil Eye 2024-11-30 13:23:47 +01:00
parent acfe9b6785
commit 8d7218c118
3 changed files with 72 additions and 31 deletions

View File

@ -1568,6 +1568,9 @@ namespace NifOsg
} }
rig->setBoneInfo(std::move(boneInfo)); rig->setBoneInfo(std::move(boneInfo));
rig->setInfluences(influences); rig->setInfluences(influences);
rig->setTransform(data->mTransform.toMatrix());
if (const Nif::NiAVObject* rootBone = skin->mRoot.getPtr())
rig->setRootBone(rootBone->mName);
drawable = rig; drawable = rig;
} }

View File

@ -7,6 +7,7 @@
#include <osgUtil/CullVisitor> #include <osgUtil/CullVisitor>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include "skeleton.hpp" #include "skeleton.hpp"
@ -181,6 +182,12 @@ namespace SceneUtil
++boneInfo; ++boneInfo;
} }
osg::Matrixf transform;
if (mSkinToSkelMatrix)
transform = (*mSkinToSkelMatrix) * mData->mTransform;
else
transform = mData->mTransform;
for (const auto& [influences, vertices] : mData->mInfluences) 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); 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; *resultMatPtr += *boneMatPtr * weight;
} }
if (mGeomToSkelMatrix) resultMat *= transform;
resultMat *= (*mGeomToSkelMatrix);
for (unsigned short vertex : vertices) for (unsigned short vertex : vertices)
{ {
@ -242,9 +248,14 @@ namespace SceneUtil
mSkeleton->updateBoneMatrices(nv->getTraversalNumber()); mSkeleton->updateBoneMatrices(nv->getTraversalNumber());
updateGeomToSkelMatrix(nv->getNodePath()); updateSkinToSkelMatrix(nv->getNodePath());
osg::BoundingBox box; osg::BoundingBox box;
osg::Matrixf transform;
if (mSkinToSkelMatrix)
transform = (*mSkinToSkelMatrix) * mData->mTransform;
else
transform = mData->mTransform;
size_t index = 0; size_t index = 0;
for (const BoneInfo& info : mData->mBones) for (const BoneInfo& info : mData->mBones)
@ -254,10 +265,7 @@ namespace SceneUtil
continue; continue;
osg::BoundingSpheref bs = info.mBoundSphere; osg::BoundingSpheref bs = info.mBoundSphere;
if (mGeomToSkelMatrix) transformBoundingSphere(bone->mMatrixInSkeletonSpace * transform, bs);
transformBoundingSphere(bone->mMatrixInSkeletonSpace * (*mGeomToSkelMatrix), bs);
else
transformBoundingSphere(bone->mMatrixInSkeletonSpace, bs);
box.expandBy(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; if (mSkinToSkelMatrix)
osg::RefMatrix* geomToSkelMatrix = mGeomToSkelMatrix; mSkinToSkelMatrix->makeIdentity();
if (geomToSkelMatrix) auto skeletonRoot = std::find(nodePath.begin(), nodePath.end(), mSkeleton);
geomToSkelMatrix->makeIdentity(); if (skeletonRoot == nodePath.end())
for (osg::NodePath::const_iterator it = nodePath.begin(); it != nodePath.end() - 1; ++it) 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; // Failed to find skin root, cancel out everything up till the trishape.
if (!foundSkel) // 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) const osg::MatrixTransform* matrixTrans = trans->asMatrixTransform();
foundSkel = true; if (matrixTrans && matrixTrans->getMatrix().isIdentity())
} continue;
else if (!mSkinToSkelMatrix)
{ mSkinToSkelMatrix = new osg::RefMatrix;
if (osg::Transform* trans = node->asTransform()) trans->computeWorldToLocalMatrix(*mSkinToSkelMatrix, nullptr);
{
osg::MatrixTransform* matrixTrans = trans->asMatrixTransform();
if (matrixTrans && matrixTrans->getMatrix().isIdentity())
continue;
if (!geomToSkelMatrix)
geomToSkelMatrix = mGeomToSkelMatrix = new osg::RefMatrix;
trans->computeWorldToLocalMatrix(*geomToSkelMatrix, nullptr);
}
} }
} }
} }
@ -352,6 +368,20 @@ namespace SceneUtil
mData->mInfluences.assign(influencesToVertices.begin(), influencesToVertices.end()); 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) void RigGeometry::accept(osg::NodeVisitor& nv)
{ {
if (!nv.validNodeMask(*this)) if (!nv.validNodeMask(*this))

View File

@ -4,6 +4,8 @@
#include <osg/Geometry> #include <osg/Geometry>
#include <osg/Matrixf> #include <osg/Matrixf>
#include <string_view>
namespace SceneUtil namespace SceneUtil
{ {
class Skeleton; class Skeleton;
@ -58,6 +60,10 @@ namespace SceneUtil
/// @note The source geometry will not be modified. /// @note The source geometry will not be modified.
void setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeom); void setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeom);
void setTransform(osg::Matrixf&& transform);
void setRootBone(std::string_view name);
osg::ref_ptr<osg::Geometry> getSourceGeometry() const; osg::ref_ptr<osg::Geometry> getSourceGeometry() const;
void accept(osg::NodeVisitor& nv) override; void accept(osg::NodeVisitor& nv) override;
@ -89,13 +95,15 @@ namespace SceneUtil
osg::ref_ptr<const osg::Vec4Array> mSourceTangents; osg::ref_ptr<const osg::Vec4Array> mSourceTangents;
Skeleton* mSkeleton{ nullptr }; Skeleton* mSkeleton{ nullptr };
osg::ref_ptr<osg::RefMatrix> mGeomToSkelMatrix; osg::ref_ptr<osg::RefMatrix> mSkinToSkelMatrix;
using VertexList = std::vector<unsigned short>; using VertexList = std::vector<unsigned short>;
struct InfluenceData : public osg::Referenced struct InfluenceData : public osg::Referenced
{ {
std::vector<BoneInfo> mBones; std::vector<BoneInfo> mBones;
std::vector<std::pair<BoneWeights, VertexList>> mInfluences; std::vector<std::pair<BoneWeights, VertexList>> mInfluences;
osg::Matrixf mTransform;
std::string mRootBone;
}; };
osg::ref_ptr<InfluenceData> mData; osg::ref_ptr<InfluenceData> mData;
std::vector<Bone*> mNodes; std::vector<Bone*> mNodes;
@ -105,7 +113,7 @@ namespace SceneUtil
bool initFromParentSkeleton(osg::NodeVisitor* nv); bool initFromParentSkeleton(osg::NodeVisitor* nv);
void updateGeomToSkelMatrix(const osg::NodePath& nodePath); void updateSkinToSkelMatrix(const osg::NodePath& nodePath);
}; };
} }