1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-14 01:19:59 +00:00

Merge branch 'smolpiranha' into 'master'

Take skin transform and skeleton root into account

Closes #4437

See merge request OpenMW/openmw!4471
This commit is contained in:
Evil Eye 2025-03-12 16:56:00 +00:00
commit 0e97af6638
3 changed files with 72 additions and 31 deletions

View File

@ -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;
}

View File

@ -7,6 +7,7 @@
#include <osgUtil/CullVisitor>
#include <components/debug/debuglog.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/resource/scenemanager.hpp>
#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))

View File

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