Merge branch 'fix-osga-rotate-wildly' into 'master'

Fix OSGAnimation issues

See merge request OpenMW/openmw!3989
This commit is contained in:
AnyOldName3 2024-04-20 15:37:09 +00:00
commit 04f1dc282b
15 changed files with 312 additions and 65 deletions

View file

@ -37,11 +37,11 @@ namespace Resource
bool RetrieveAnimationsVisitor::belongsToLeftUpperExtremity(const std::string& name)
{
static const std::array boneNames = { "bip01_l_clavicle", "left_clavicle", "bip01_l_upperarm", "left_upper_arm",
"bip01_l_forearm", "bip01_l_hand", "left_hand", "left_wrist", "shield_bone", "bip01_l_pinky1",
"bip01_l_pinky2", "bip01_l_pinky3", "bip01_l_ring1", "bip01_l_ring2", "bip01_l_ring3", "bip01_l_middle1",
"bip01_l_middle2", "bip01_l_middle3", "bip01_l_pointer1", "bip01_l_pointer2", "bip01_l_pointer3",
"bip01_l_thumb1", "bip01_l_thumb2", "bip01_l_thumb3", "left_forearm" };
static const std::array boneNames = { "bip01 l clavicle", "left clavicle", "bip01 l upperarm", "left upper arm",
"bip01 l forearm", "bip01 l hand", "left hand", "left wrist", "shield bone", "bip01 l pinky1",
"bip01 l pinky2", "bip01 l pinky3", "bip01 l ring1", "bip01 l ring2", "bip01 l ring3", "bip01 l middle1",
"bip01 l middle2", "bip01 l middle3", "bip01 l pointer1", "bip01 l pointer2", "bip01 l pointer3",
"bip01 l thumb1", "bip01 l thumb2", "bip01 l thumb3", "left forearm" };
if (std::find(boneNames.begin(), boneNames.end(), name) != boneNames.end())
return true;
@ -51,11 +51,11 @@ namespace Resource
bool RetrieveAnimationsVisitor::belongsToRightUpperExtremity(const std::string& name)
{
static const std::array boneNames = { "bip01_r_clavicle", "right_clavicle", "bip01_r_upperarm",
"right_upper_arm", "bip01_r_forearm", "bip01_r_hand", "right_hand", "right_wrist", "bip01_r_thumb1",
"bip01_r_thumb2", "bip01_r_thumb3", "weapon_bone", "bip01_r_pinky1", "bip01_r_pinky2", "bip01_r_pinky3",
"bip01_r_ring1", "bip01_r_ring2", "bip01_r_ring3", "bip01_r_middle1", "bip01_r_middle2", "bip01_r_middle3",
"bip01_r_pointer1", "bip01_r_pointer2", "bip01_r_pointer3", "right_forearm" };
static const std::array boneNames = { "bip01 r clavicle", "right clavicle", "bip01 r upperarm",
"right upper arm", "bip01 r forearm", "bip01 r hand", "right hand", "right wrist", "bip01 r thumb1",
"bip01 r thumb2", "bip01 r thumb3", "weapon bone", "bip01 r pinky1", "bip01 r pinky2", "bip01 r pinky3",
"bip01 r ring1", "bip01 r ring2", "bip01 r ring3", "bip01 r middle1", "bip01 r middle2", "bip01 r middle3",
"bip01 r pointer1", "bip01 r pointer2", "bip01 r pointer3", "right forearm" };
if (std::find(boneNames.begin(), boneNames.end(), name) != boneNames.end())
return true;
@ -66,7 +66,7 @@ namespace Resource
bool RetrieveAnimationsVisitor::belongsToTorso(const std::string& name)
{
static const std::array boneNames
= { "bip01_spine1", "bip01_spine2", "bip01_neck", "bip01_head", "head", "neck", "chest", "groin" };
= { "bip01 spine1", "bip01 spine2", "bip01 neck", "bip01 head", "head", "neck", "chest", "groin" };
if (std::find(boneNames.begin(), boneNames.end(), name) != boneNames.end())
return true;
@ -99,6 +99,9 @@ namespace Resource
const osgAnimation::ChannelList& channels = animation->getChannels();
for (const auto& channel : channels)
{
// Replace channel target name to match the renamed bones/transforms
channel->setTargetName(Misc::StringUtils::underscoresToSpaces(channel->getTargetName()));
if (name == "Bip01 R Clavicle")
{
if (!belongsToRightUpperExtremity(channel->getTargetName()))

View file

@ -9,7 +9,10 @@
#include <osg/Node>
#include <osg/UserDataContainer>
#include <osgAnimation/Bone>
#include <osgAnimation/RigGeometry>
#include <osgAnimation/Skeleton>
#include <osgAnimation/UpdateBone>
#include <osgParticle/ParticleSystem>
@ -268,6 +271,11 @@ namespace Resource
void apply(osg::Node& node) override
{
// If an osgAnimation bone/transform, ensure underscores in name are replaced with spaces
// this is for compatibility reasons
if (dynamic_cast<osgAnimation::Bone*>(&node))
node.setName(Misc::StringUtils::underscoresToSpaces(node.getName()));
if (osg::StateSet* stateset = node.getStateSet())
{
if (stateset->getRenderingHint() == osg::StateSet::TRANSPARENT_BIN)
@ -353,6 +361,53 @@ namespace Resource
std::vector<osg::ref_ptr<SceneUtil::RigGeometryHolder>> mRigGeometryHolders;
};
void updateVertexInfluenceMap(osgAnimation::RigGeometry& rig)
{
osgAnimation::VertexInfluenceMap* vertexInfluenceMap = rig.getInfluenceMap();
if (!vertexInfluenceMap)
return;
std::vector<std::string> renameList;
for (const auto& [boneName, unused] : *vertexInfluenceMap)
{
if (boneName.find('_') != std::string::npos)
renameList.push_back(boneName);
}
for (const std::string& oldName : renameList)
{
const std::string newName = Misc::StringUtils::underscoresToSpaces(oldName);
if (vertexInfluenceMap->find(newName) == vertexInfluenceMap->end())
(*vertexInfluenceMap)[newName] = std::move((*vertexInfluenceMap)[oldName]);
vertexInfluenceMap->erase(oldName);
}
}
class RenameAnimCallbacksVisitor : public osg::NodeVisitor
{
public:
RenameAnimCallbacksVisitor()
: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
{
}
void apply(osg::MatrixTransform& node) override
{
// osgAnimation update callback name must match bone name/channel targets
osg::Callback* cb = node.getUpdateCallback();
while (cb)
{
auto animCb = dynamic_cast<osgAnimation::AnimationUpdateCallback<osg::NodeCallback>*>(cb);
if (animCb)
animCb->setName(Misc::StringUtils::underscoresToSpaces(animCb->getName()));
cb = cb->getNestedCallback();
}
traverse(node);
}
};
SceneManager::SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager,
Resource::NifFileManager* nifFileManager, double expiryDelay)
: ResourceManager(vfs, expiryDelay)
@ -556,6 +611,7 @@ namespace Resource
VFS::Path::NormalizedView normalizedFilename, std::istream& model, Resource::ImageManager* imageManager)
{
const std::string_view ext = Misc::getFileExtension(normalizedFilename.value());
const bool isColladaFile = ext == "dae";
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(std::string(ext));
if (!reader)
{
@ -571,7 +627,7 @@ namespace Resource
// findFileCallback would be necessary. but findFileCallback does not support virtual files, so we can't
// implement it.
options->setReadFileCallback(new ImageReadCallback(imageManager));
if (ext == "dae")
if (isColladaFile)
options->setOptionString("daeUseSequencedTextureUnits");
const std::array<std::uint64_t, 2> fileHash = Files::getHash(normalizedFilename.value(), model);
@ -599,9 +655,13 @@ namespace Resource
node->accept(rigFinder);
for (osg::Node* foundRigNode : rigFinder.mFoundNodes)
{
if (foundRigNode->libraryName() == std::string("osgAnimation"))
if (foundRigNode->libraryName() == std::string_view("osgAnimation"))
{
osgAnimation::RigGeometry* foundRigGeometry = static_cast<osgAnimation::RigGeometry*>(foundRigNode);
if (isColladaFile)
Resource::updateVertexInfluenceMap(*foundRigGeometry);
osg::ref_ptr<SceneUtil::RigGeometryHolder> newRig
= new SceneUtil::RigGeometryHolder(*foundRigGeometry, osg::CopyOp::DEEP_COPY_ALL);
@ -616,13 +676,18 @@ namespace Resource
}
}
if (ext == "dae")
if (isColladaFile)
{
Resource::ColladaDescriptionVisitor colladaDescriptionVisitor;
node->accept(colladaDescriptionVisitor);
if (colladaDescriptionVisitor.mSkeleton)
{
// Collada bones may have underscores in place of spaces due to a collada limitation
// we should rename the bones and update callbacks here at load time
Resource::RenameAnimCallbacksVisitor renameBoneVisitor;
node->accept(renameBoneVisitor);
if (osg::Group* group = dynamic_cast<osg::Group*>(node))
{
group->removeChildren(0, group->getNumChildren());