#include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace SceneUtil { LinkVisitor::LinkVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { mAnimation = nullptr; } void LinkVisitor::link(osgAnimation::UpdateMatrixTransform* umt) { const osgAnimation::ChannelList& channels = mAnimation->getChannels(); for (const auto& channel : channels) { const std::string& channelName = channel->getName(); const std::string& channelTargetName = channel->getTargetName(); if (channelTargetName != umt->getName()) continue; // check if we can link a StackedTransformElement to the current Channel for (const auto& stackedTransform : umt->getStackedTransforms()) { osgAnimation::StackedTransformElement* element = stackedTransform.get(); if (element && !element->getName().empty() && channelName == element->getName()) { osgAnimation::Target* target = element->getOrCreateTarget(); if (target) { channel->setTarget(target); } } } } } void LinkVisitor::setAnimation(Resource::Animation* animation) { mAnimation = animation; } void LinkVisitor::apply(osg::Node& node) { osg::Callback* cb = node.getUpdateCallback(); while (cb) { osgAnimation::UpdateMatrixTransform* umt = dynamic_cast(cb); if (umt) if (Misc::StringUtils::lowerCase(node.getName()) != "bip01") link(umt); cb = cb->getNestedCallback(); } if (node.getNumChildrenRequiringUpdateTraversal()) traverse(node); } OsgAnimationController::OsgAnimationController(const OsgAnimationController& copy, const osg::CopyOp& copyop) : osg::Object(copy, copyop) , SceneUtil::KeyframeController(copy) , SceneUtil::NodeCallback(copy, copyop) , mEmulatedAnimations(copy.mEmulatedAnimations) { mLinker = nullptr; for (const auto& mergedAnimationTrack : copy.mMergedAnimationTracks) { Resource::Animation* copiedAnimationTrack = static_cast(mergedAnimationTrack.get()->clone(copyop)); mMergedAnimationTracks.emplace_back(copiedAnimationTrack); } } osg::Matrixf OsgAnimationController::getTransformForNode(float time, const std::string_view name) const { std::string animationName; float newTime = time; // Find the correct animation based on time for (const EmulatedAnimation& emulatedAnimation : mEmulatedAnimations) { if (time >= emulatedAnimation.mStartTime && time <= emulatedAnimation.mStopTime) { newTime = time - emulatedAnimation.mStartTime; animationName = emulatedAnimation.mName; break; } } // Find the bone's transform track in animation for (const auto& mergedAnimationTrack : mMergedAnimationTracks) { if (mergedAnimationTrack->getName() != animationName) continue; const osgAnimation::ChannelList& channels = mergedAnimationTrack->getChannels(); for (const auto& channel : channels) { if (!Misc::StringUtils::ciEqual(name, channel->getTargetName()) || channel->getName() != "transform") continue; if (osgAnimation::MatrixLinearSampler* templateSampler = dynamic_cast(channel->getSampler())) { osg::Matrixf matrix; templateSampler->getValueAt(newTime, matrix); return matrix; } } } return osg::Matrixf::identity(); } osg::Vec3f OsgAnimationController::getTranslation(float time) const { return getTransformForNode(time, "bip01").getTrans(); } void OsgAnimationController::update(float time, const std::string& animationName) { for (const auto& mergedAnimationTrack : mMergedAnimationTracks) { if (mergedAnimationTrack->getName() == animationName) mergedAnimationTrack->update(time); } } void OsgAnimationController::operator()(osg::Node* node, osg::NodeVisitor* nv) { if (hasInput()) { if (mNeedToLink) { for (const auto& mergedAnimationTrack : mMergedAnimationTracks) { if (!mLinker.valid()) mLinker = new LinkVisitor(); mLinker->setAnimation(mergedAnimationTrack); node->accept(*mLinker); } mNeedToLink = false; } float time = getInputValue(nv); for (const EmulatedAnimation& emulatedAnimation : mEmulatedAnimations) { if (time > emulatedAnimation.mStartTime && time < emulatedAnimation.mStopTime) { update(time - emulatedAnimation.mStartTime, emulatedAnimation.mName); } } // Reset the transform of this node to whats in the animation // we force this here because downstream some code relies on the bone having a non-modified transform // as this is how the NIF controller behaves. RotationController is a good example of this. // Without this here, it causes osgAnimation skeletons to spin wildly static_cast(node)->setMatrix(getTransformForNode(time, node->getName())); } traverse(node, nv); } void OsgAnimationController::setEmulatedAnimations(const std::vector& emulatedAnimations) { mEmulatedAnimations = emulatedAnimations; } void OsgAnimationController::addMergedAnimationTrack(osg::ref_ptr animationTrack) { mMergedAnimationTracks.emplace_back(animationTrack); } }