mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-29 17:15:34 +00:00
Merge branch 'fix-osga-rotate-wildly' into 'master'
Fix OSGAnimation issues See merge request OpenMW/openmw!3989
This commit is contained in:
commit
04f1dc282b
15 changed files with 312 additions and 65 deletions
|
@ -46,6 +46,7 @@
|
|||
Bug #6657: Distant terrain tiles become black when using FWIW mod
|
||||
Bug #6661: Saved games that have no preview screenshot cause issues or crashes
|
||||
Bug #6716: mwscript comparison operator handling is too restrictive
|
||||
Bug #6723: "Turn to movement direction" makes the player rotate wildly with COLLADA
|
||||
Bug #6754: Beast to Non-beast transformation mod is not working on OpenMW
|
||||
Bug #6758: Main menu background video can be stopped by opening the options menu
|
||||
Bug #6807: Ultimate Galleon is not working properly
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace CSVRender
|
|||
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
|
||||
SceneUtil::Skeleton* mSkeleton;
|
||||
SceneUtil::NodeMapVisitor::NodeMap mNodeMap;
|
||||
SceneUtil::NodeMap mNodeMap;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -530,6 +530,7 @@ namespace MWRender
|
|||
, mHasMagicEffects(false)
|
||||
, mAlpha(1.f)
|
||||
, mPlayScriptedOnly(false)
|
||||
, mRequiresBoneMap(false)
|
||||
{
|
||||
for (size_t i = 0; i < sNumBlendMasks; i++)
|
||||
mAnimationTimePtr[i] = std::make_shared<AnimationTime>();
|
||||
|
@ -964,8 +965,17 @@ namespace MWRender
|
|||
{
|
||||
if (!mNodeMapCreated && mObjectRoot)
|
||||
{
|
||||
SceneUtil::NodeMapVisitor visitor(mNodeMap);
|
||||
mObjectRoot->accept(visitor);
|
||||
// If the base of this animation is an osgAnimation, we should map the bones not matrix transforms
|
||||
if (mRequiresBoneMap)
|
||||
{
|
||||
SceneUtil::NodeMapVisitorBoneOnly visitor(mNodeMap);
|
||||
mObjectRoot->accept(visitor);
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneUtil::NodeMapVisitor visitor(mNodeMap);
|
||||
mObjectRoot->accept(visitor);
|
||||
}
|
||||
mNodeMapCreated = true;
|
||||
}
|
||||
return mNodeMap;
|
||||
|
@ -1479,6 +1489,10 @@ namespace MWRender
|
|||
mInsert->addChild(mObjectRoot);
|
||||
}
|
||||
|
||||
// osgAnimation formats with skeletons should have their nodemap be bone instances
|
||||
// FIXME: better way to detect osgAnimation here instead of relying on extension?
|
||||
mRequiresBoneMap = mSkeleton != nullptr && !Misc::StringUtils::ciEndsWith(model, ".nif");
|
||||
|
||||
if (previousStateset)
|
||||
mObjectRoot->setStateSet(previousStateset);
|
||||
|
||||
|
|
|
@ -246,6 +246,7 @@ namespace MWRender
|
|||
osg::ref_ptr<SceneUtil::LightListCallback> mLightListCallback;
|
||||
|
||||
bool mPlayScriptedOnly;
|
||||
bool mRequiresBoneMap;
|
||||
|
||||
const NodeMap& getNodeMap() const;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "rotatecontroller.hpp"
|
||||
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osgAnimation/Bone>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
@ -43,6 +44,17 @@ namespace MWRender
|
|||
|
||||
node->setMatrix(matrix);
|
||||
|
||||
// If we are linked to a bone we must call setMatrixInSkeletonSpace
|
||||
osgAnimation::Bone* b = dynamic_cast<osgAnimation::Bone*>(node);
|
||||
if (b)
|
||||
{
|
||||
osgAnimation::Bone* parent = b->getBoneParent();
|
||||
if (parent)
|
||||
matrix *= parent->getMatrixInSkeletonSpace();
|
||||
|
||||
b->setMatrixInSkeletonSpace(matrix);
|
||||
}
|
||||
|
||||
traverse(node, nv);
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,8 @@ file(GLOB UNITTEST_SRC_FILES
|
|||
resource/testobjectcache.cpp
|
||||
|
||||
vfs/testpathutil.cpp
|
||||
|
||||
sceneutil/osgacontroller.cpp
|
||||
)
|
||||
|
||||
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})
|
||||
|
|
131
apps/openmw_test_suite/sceneutil/osgacontroller.cpp
Normal file
131
apps/openmw_test_suite/sceneutil/osgacontroller.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
#include <components/sceneutil/osgacontroller.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <osgAnimation/Channel>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace SceneUtil;
|
||||
|
||||
static const std::string ROOT_BONE_NAME = "bip01";
|
||||
|
||||
// Creates a merged anim track with a single root channel with two start/end matrix transforms
|
||||
osg::ref_ptr<Resource::Animation> createMergedAnimationTrack(std::string name, osg::Matrixf startTransform,
|
||||
osg::Matrixf endTransform, float startTime = 0.0f, float endTime = 1.0f)
|
||||
{
|
||||
osg::ref_ptr<Resource::Animation> mergedAnimationTrack = new Resource::Animation;
|
||||
mergedAnimationTrack->setName(name);
|
||||
|
||||
osgAnimation::MatrixKeyframeContainer* cbCntr = new osgAnimation::MatrixKeyframeContainer;
|
||||
cbCntr->push_back(osgAnimation::MatrixKeyframe(startTime, startTransform));
|
||||
cbCntr->push_back(osgAnimation::MatrixKeyframe(endTime, endTransform));
|
||||
|
||||
osg::ref_ptr<osgAnimation::MatrixLinearChannel> rootChannel = new osgAnimation::MatrixLinearChannel;
|
||||
rootChannel->setName("transform");
|
||||
rootChannel->setTargetName(ROOT_BONE_NAME);
|
||||
rootChannel->getOrCreateSampler()->setKeyframeContainer(cbCntr);
|
||||
mergedAnimationTrack->addChannel(rootChannel);
|
||||
return mergedAnimationTrack;
|
||||
}
|
||||
|
||||
TEST(OsgAnimationControllerTest, getTranslationShouldReturnSampledChannelTranslationForBip01)
|
||||
{
|
||||
std::vector<EmulatedAnimation> emulatedAnimations;
|
||||
emulatedAnimations.push_back({ 0.0f, 1.0f, "test1" }); // should sample this
|
||||
emulatedAnimations.push_back({ 1.1f, 2.0f, "test2" }); // should ignore this
|
||||
|
||||
OsgAnimationController controller;
|
||||
controller.setEmulatedAnimations(emulatedAnimations);
|
||||
|
||||
osg::Matrixf startTransform = osg::Matrixf::identity();
|
||||
osg::Matrixf endTransform = osg::Matrixf::identity();
|
||||
osg::Matrixf endTransform2 = osg::Matrixf::identity();
|
||||
endTransform.setTrans(1.0f, 1.0f, 1.0f);
|
||||
controller.addMergedAnimationTrack(createMergedAnimationTrack("test1", startTransform, endTransform));
|
||||
endTransform2.setTrans(2.0f, 2.0f, 2.0f);
|
||||
controller.addMergedAnimationTrack(
|
||||
createMergedAnimationTrack("test2", endTransform, endTransform2, 0.1f, 0.9f));
|
||||
|
||||
// should be halfway between 0,0,0 and 1,1,1
|
||||
osg::Vec3f translation = controller.getTranslation(0.5f);
|
||||
EXPECT_EQ(translation, osg::Vec3f(0.5f, 0.5f, 0.5f));
|
||||
}
|
||||
|
||||
TEST(OsgAnimationControllerTest, getTranslationShouldReturnZeroVectorIfNotFound)
|
||||
{
|
||||
std::vector<EmulatedAnimation> emulatedAnimations;
|
||||
emulatedAnimations.push_back({ 0.0f, 1.0f, "test1" });
|
||||
|
||||
OsgAnimationController controller;
|
||||
controller.setEmulatedAnimations(emulatedAnimations);
|
||||
|
||||
osg::Matrixf startTransform = osg::Matrixf::identity();
|
||||
osg::Matrixf endTransform = osg::Matrixf::identity();
|
||||
endTransform.setTrans(1.0f, 1.0f, 1.0f);
|
||||
controller.addMergedAnimationTrack(createMergedAnimationTrack("test1", startTransform, endTransform));
|
||||
|
||||
// Has no emulated animation at time so will return 0,0,0
|
||||
osg::Vec3f translation = controller.getTranslation(100.0f);
|
||||
EXPECT_EQ(translation, osg::Vec3f(0.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
TEST(OsgAnimationControllerTest, getTranslationShouldReturnZeroVectorIfNoMergedTracks)
|
||||
{
|
||||
std::vector<EmulatedAnimation> emulatedAnimations;
|
||||
emulatedAnimations.push_back({ 0.0f, 1.0f, "test1" });
|
||||
|
||||
OsgAnimationController controller;
|
||||
controller.setEmulatedAnimations(emulatedAnimations);
|
||||
|
||||
// Has no merged tracks so will return 0,0,0
|
||||
osg::Vec3f translation = controller.getTranslation(0.5);
|
||||
EXPECT_EQ(translation, osg::Vec3f(0.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
TEST(OsgAnimationControllerTest, getTransformShouldReturnIdentityIfNotFound)
|
||||
{
|
||||
std::vector<EmulatedAnimation> emulatedAnimations;
|
||||
emulatedAnimations.push_back({ 0.0f, 1.0f, "test1" });
|
||||
|
||||
OsgAnimationController controller;
|
||||
controller.setEmulatedAnimations(emulatedAnimations);
|
||||
|
||||
osg::Matrixf startTransform = osg::Matrixf::identity();
|
||||
osg::Matrixf endTransform = osg::Matrixf::identity();
|
||||
endTransform.setTrans(1.0f, 1.0f, 1.0f);
|
||||
controller.addMergedAnimationTrack(createMergedAnimationTrack("test1", startTransform, endTransform));
|
||||
|
||||
// Has no emulated animation at time so will return identity
|
||||
EXPECT_EQ(controller.getTransformForNode(100.0f, ROOT_BONE_NAME), osg::Matrixf::identity());
|
||||
|
||||
// Has no bone animation at time so will return identity
|
||||
EXPECT_EQ(controller.getTransformForNode(0.5f, "wrongbone"), osg::Matrixf::identity());
|
||||
}
|
||||
|
||||
TEST(OsgAnimationControllerTest, getTransformShouldReturnSampledAnimMatrixAtTime)
|
||||
{
|
||||
std::vector<EmulatedAnimation> emulatedAnimations;
|
||||
emulatedAnimations.push_back({ 0.0f, 1.0f, "test1" }); // should sample this
|
||||
emulatedAnimations.push_back({ 1.1f, 2.0f, "test2" }); // should ignore this
|
||||
|
||||
OsgAnimationController controller;
|
||||
controller.setEmulatedAnimations(emulatedAnimations);
|
||||
|
||||
osg::Matrixf startTransform = osg::Matrixf::identity();
|
||||
osg::Matrixf endTransform = osg::Matrixf::identity();
|
||||
endTransform.setTrans(1.0f, 1.0f, 1.0f);
|
||||
controller.addMergedAnimationTrack(createMergedAnimationTrack("test1", startTransform, endTransform));
|
||||
osg::Matrixf endTransform2 = osg::Matrixf::identity();
|
||||
endTransform2.setTrans(2.0f, 2.0f, 2.0f);
|
||||
controller.addMergedAnimationTrack(
|
||||
createMergedAnimationTrack("test2", endTransform, endTransform2, 0.1f, 0.9f));
|
||||
|
||||
EXPECT_EQ(controller.getTransformForNode(0.0f, ROOT_BONE_NAME), startTransform); // start of test1
|
||||
EXPECT_EQ(controller.getTransformForNode(1.0f, ROOT_BONE_NAME), endTransform); // end of test1
|
||||
EXPECT_EQ(controller.getTransformForNode(1.1f, ROOT_BONE_NAME), endTransform); // start of test2
|
||||
EXPECT_EQ(controller.getTransformForNode(2.0f, ROOT_BONE_NAME), endTransform2); // end of test2
|
||||
}
|
||||
}
|
|
@ -15,6 +15,13 @@ namespace Misc::StringUtils
|
|||
bool operator()(char x, char y) const { return toLower(x) < toLower(y); }
|
||||
};
|
||||
|
||||
inline std::string underscoresToSpaces(const std::string_view oldName)
|
||||
{
|
||||
std::string newName(oldName);
|
||||
std::replace(newName.begin(), newName.end(), '_', ' ');
|
||||
return newName;
|
||||
}
|
||||
|
||||
inline bool ciLess(std::string_view x, std::string_view y)
|
||||
{
|
||||
return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), CiCharLess());
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include <components/misc/osguservalues.hpp>
|
||||
#include <components/misc/strings/algorithm.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <components/sceneutil/osgacontroller.hpp>
|
||||
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Node>
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/ref_ptr>
|
||||
|
@ -9,6 +10,7 @@
|
|||
#include <osgAnimation/Sampler>
|
||||
#include <osgAnimation/UpdateMatrixTransform>
|
||||
|
||||
#include <components/misc/strings/algorithm.hpp>
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
#include <components/resource/animation.hpp>
|
||||
#include <components/sceneutil/controller.hpp>
|
||||
|
@ -24,6 +26,10 @@ namespace SceneUtil
|
|||
|
||||
void LinkVisitor::link(osgAnimation::UpdateMatrixTransform* umt)
|
||||
{
|
||||
// If osgAnimation had underscores, we should update the umt name also
|
||||
// otherwise the animation channel and updates wont be applied
|
||||
umt->setName(Misc::StringUtils::underscoresToSpaces(umt->getName()));
|
||||
|
||||
const osgAnimation::ChannelList& channels = mAnimation->getChannels();
|
||||
for (const auto& channel : channels)
|
||||
{
|
||||
|
@ -85,9 +91,8 @@ namespace SceneUtil
|
|||
}
|
||||
}
|
||||
|
||||
osg::Vec3f OsgAnimationController::getTranslation(float time) const
|
||||
osg::Matrixf OsgAnimationController::getTransformForNode(float time, const std::string_view name) const
|
||||
{
|
||||
osg::Vec3f translationValue;
|
||||
std::string animationName;
|
||||
float newTime = time;
|
||||
|
||||
|
@ -98,10 +103,11 @@ namespace SceneUtil
|
|||
{
|
||||
newTime = time - emulatedAnimation.mStartTime;
|
||||
animationName = emulatedAnimation.mName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the root transform track in animation
|
||||
// Find the bone's transform track in animation
|
||||
for (const auto& mergedAnimationTrack : mMergedAnimationTracks)
|
||||
{
|
||||
if (mergedAnimationTrack->getName() != animationName)
|
||||
|
@ -111,7 +117,7 @@ namespace SceneUtil
|
|||
|
||||
for (const auto& channel : channels)
|
||||
{
|
||||
if (channel->getTargetName() != "bip01" || channel->getName() != "transform")
|
||||
if (!Misc::StringUtils::ciEqual(name, channel->getTargetName()) || channel->getName() != "transform")
|
||||
continue;
|
||||
|
||||
if (osgAnimation::MatrixLinearSampler* templateSampler
|
||||
|
@ -119,13 +125,17 @@ namespace SceneUtil
|
|||
{
|
||||
osg::Matrixf matrix;
|
||||
templateSampler->getValueAt(newTime, matrix);
|
||||
translationValue = matrix.getTrans();
|
||||
return osg::Vec3f(translationValue[0], translationValue[1], translationValue[2]);
|
||||
return matrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return osg::Vec3f();
|
||||
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)
|
||||
|
@ -162,6 +172,12 @@ namespace SceneUtil
|
|||
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<osg::MatrixTransform*>(node)->setMatrix(getTransformForNode(time, node->getName()));
|
||||
}
|
||||
|
||||
traverse(node, nv);
|
||||
|
|
|
@ -59,6 +59,9 @@ namespace SceneUtil
|
|||
/// @brief Handles the location of the instance
|
||||
osg::Vec3f getTranslation(float time) const override;
|
||||
|
||||
/// @brief Handles finding bone position in the animation
|
||||
osg::Matrixf getTransformForNode(float time, const std::string_view name) const;
|
||||
|
||||
/// @brief Calls animation track update()
|
||||
void update(float time, const std::string& animationName);
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include <osgParticle/ParticleSystem>
|
||||
|
||||
#include <osgAnimation/Bone>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/strings/algorithm.hpp>
|
||||
|
||||
|
@ -13,7 +15,6 @@
|
|||
|
||||
namespace SceneUtil
|
||||
{
|
||||
|
||||
bool FindByNameVisitor::checkGroup(osg::Group& group)
|
||||
{
|
||||
if (Misc::StringUtils::ciEqual(group.getName(), mNameToFind))
|
||||
|
@ -22,35 +23,13 @@ namespace SceneUtil
|
|||
return true;
|
||||
}
|
||||
|
||||
// FIXME: can the nodes/bones be renamed at loading stage rather than each time?
|
||||
// Convert underscores to whitespaces as a workaround for Collada (OpenMW's animation system uses
|
||||
// whitespace-separated names)
|
||||
std::string nodeName = group.getName();
|
||||
std::replace(nodeName.begin(), nodeName.end(), '_', ' ');
|
||||
if (Misc::StringUtils::ciEqual(nodeName, mNameToFind))
|
||||
{
|
||||
mFoundNode = &group;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FindByClassVisitor::apply(osg::Node& node)
|
||||
{
|
||||
if (Misc::StringUtils::ciEqual(node.className(), mNameToFind))
|
||||
{
|
||||
mFoundNodes.push_back(&node);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: can the nodes/bones be renamed at loading stage rather than each time?
|
||||
// Convert underscores to whitespaces as a workaround for Collada (OpenMW's animation system uses
|
||||
// whitespace-separated names)
|
||||
std::string nodeName = node.className();
|
||||
std::replace(nodeName.begin(), nodeName.end(), '_', ' ');
|
||||
if (Misc::StringUtils::ciEqual(nodeName, mNameToFind))
|
||||
mFoundNodes.push_back(&node);
|
||||
}
|
||||
|
||||
traverse(node);
|
||||
}
|
||||
|
@ -69,23 +48,19 @@ namespace SceneUtil
|
|||
|
||||
void FindByNameVisitor::apply(osg::Geometry&) {}
|
||||
|
||||
void NodeMapVisitorBoneOnly::apply(osg::MatrixTransform& trans)
|
||||
{
|
||||
// Choose first found bone in file
|
||||
if (dynamic_cast<osgAnimation::Bone*>(&trans) != nullptr)
|
||||
mMap.emplace(trans.getName(), &trans);
|
||||
|
||||
traverse(trans);
|
||||
}
|
||||
|
||||
void NodeMapVisitor::apply(osg::MatrixTransform& trans)
|
||||
{
|
||||
// Choose first found node in file
|
||||
|
||||
if (trans.libraryName() == std::string_view("osgAnimation"))
|
||||
{
|
||||
std::string nodeName = trans.getName();
|
||||
|
||||
// FIXME: can the nodes/bones be renamed at loading stage rather than each time?
|
||||
// Convert underscores to whitespaces as a workaround for Collada (OpenMW's animation system uses
|
||||
// whitespace-separated names)
|
||||
std::replace(nodeName.begin(), nodeName.end(), '_', ' ');
|
||||
mMap.emplace(nodeName, &trans);
|
||||
}
|
||||
else
|
||||
mMap.emplace(trans.getName(), &trans);
|
||||
|
||||
mMap.emplace(trans.getName(), &trans);
|
||||
traverse(trans);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,14 +50,14 @@ namespace SceneUtil
|
|||
std::vector<osg::Node*> mFoundNodes;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<std::string, osg::ref_ptr<osg::MatrixTransform>, Misc::StringUtils::CiHash,
|
||||
Misc::StringUtils::CiEqual>
|
||||
NodeMap;
|
||||
|
||||
/// Maps names to nodes
|
||||
class NodeMapVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
typedef std::unordered_map<std::string, osg::ref_ptr<osg::MatrixTransform>, Misc::StringUtils::CiHash,
|
||||
Misc::StringUtils::CiEqual>
|
||||
NodeMap;
|
||||
|
||||
NodeMapVisitor(NodeMap& map)
|
||||
: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
|
||||
, mMap(map)
|
||||
|
@ -70,6 +70,22 @@ namespace SceneUtil
|
|||
NodeMap& mMap;
|
||||
};
|
||||
|
||||
/// Maps names to bone nodes
|
||||
class NodeMapVisitorBoneOnly : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
NodeMapVisitorBoneOnly(NodeMap& map)
|
||||
: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
|
||||
, mMap(map)
|
||||
{
|
||||
}
|
||||
|
||||
void apply(osg::MatrixTransform& trans) override;
|
||||
|
||||
private:
|
||||
NodeMap& mMap;
|
||||
};
|
||||
|
||||
/// @brief Base class for visitors that remove nodes from a scene graph.
|
||||
/// Subclasses need to fill the mToRemove vector.
|
||||
/// To use, node->accept(removeVisitor); removeVisitor.remove();
|
||||
|
|
Loading…
Reference in a new issue