how to get each bone's location and rotation during animations?

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


knn217
Halfling
Posts: 78
Joined: Wed Jan 25, 2023 9:04 am
x 5

how to get each bone's location and rotation during animations?

Post by knn217 »

I want to get each bone's location and rotation each frame so I tried to put it in generateDebugText using this line:

Code: Select all

// 14 bones
for (int i = 0; i <= 13; i++) 
        {
            outText += "\n";
            outText += Ogre::StringConverter::toString(mItem->getSkeletonInstance()->getDefinition()->getBones()[i].vPos);
            outText += "\n";
            outText += Ogre::StringConverter::toString(mItem->getSkeletonInstance()->getDefinition()->getBones()[i].qRot);
        }

It did print some location and rotation vectors to the screen but they don't change even though the animation is running and the object is moving around.

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5476
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1358

Re: how to get each bone's location and rotation during animations?

Post by dark_sylinc »

Hi!

You're grabbing the data from the definition which is the base data, not the instance data.

You need to do:

Code: Select all

Ogre::SkeletonInstance *skeletonInstance = stickmanItem->getSkeletonInstance();
Ogre::Bone *bone = skeletonInstance->getBone( "Hand_L" );
Ogre::Matrix4 boneTransform = bone->_getDerivedTransform();

// Position
Ogre::Vector3 vPos = boneTransform.getTrans();
// Orientation + Scale
Ogre::Matrix3 rotScaleMat;
boneTransform.extract3x3Matrix( rotScaleMat );

// WARNING: This will only work correctly as long as the skeleton animation has uniform scaling
// If the bones apply non-uniform scaling, then the Quaternion and Scale will be wrong
// (non-uniform scaling cannot be properly represented using Scale + Quaternion)
Ogre::Vector3 vScale;
Ogre::Quaternion qRot;
boneTransform.decomposition( vPos, vScale, qRot );
knn217
Halfling
Posts: 78
Joined: Wed Jan 25, 2023 9:04 am
x 5

Re: how to get each bone's location and rotation during animations?

Post by knn217 »

Thanks for the help, now the positions and rotations are getting updated.

As I understand, this method is getting the pos, scale and rot while they're being updated during the animation right?

Is there a method that can get me the position in each frame from the .skeleton file alone, without even playing the animation?
I figured this should be quite tedious, but possible since the vectors for each frame are already in the .skeleton file without playing the animation.
The position in this case is the offset from the mesh's reference point (I dunno if the mesh has a reference point itself or is it just the root bone), not the position in the scene.

knn217
Halfling
Posts: 78
Joined: Wed Jan 25, 2023 9:04 am
x 5

Re: how to get each bone's location and rotation during animations?

Post by knn217 »

for more context, I'm trying to make a fighting game with rollback netcode and right now, I'm trying to solve the hitbox/hurtbox problem. For rollback to work, I need to separate logic(hit/hurtbox) from render(animation), I can probably just make a random hit/hurtbox that mostly covers the animation like the old games used to do. But before doing that, I'm trying to see if I can make the hit/hurtbox to be precise to the animation, that's why I'm trying to get the bones positions.

There are 2 ways I've came up with:

  1. During scene creation, load each animation and get the bone positions in each frame. This method is tedious but probably optimal for speed so i"m trying to achieve this. I need to get the position data for each frame from the .skeleton file during the loading phase.

  2. Render the animation as fast as possible, get the bone positions each frame it is updated and send them to the logic thread (or just load the skeleton in the logic thread itself to avoid sending data). I'm trying to avoid this since I'm pretty sure it's gonna be slower than the 1st method

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5476
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1358

Re: how to get each bone's location and rotation during animations?

Post by dark_sylinc »

I had to solve a very similar problem so what I ended up doing was cloning the SkeletonDef and doing what Ogre does, on the Logic side:

Code: Select all

//--------------------------------------------------------------------------------
// At Logic manager level
//--------------------------------------------------------------------------------
Ogre::SkeletonAnimManager *m_logicSkeletonAnimManager = new Ogre::SkeletonAnimManager();

//--------------------------------------------------------------------------------
// Per type of Skeleton
//--------------------------------------------------------------------------------
Ogre::v1::Skeleton *originalV1Skeleton = static_pointer_cast<Skeleton>(
						 Ogre::v1::OldSkeletonManager::getSingleton().load(
							 skeletonName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ) )
						 .get();

originalV1Skeleton->setBlendMode( Ogre::v1::ANIMBLEND_CUMULATIVE );
		
m_skeletonDef = OGRE_NEW Ogre::SkeletonDef( originalV1Skeleton, 1.0f );
originalV1Skeleton->unload(); // Not needed anymore

//--------------------------------------------------------------------------------
// Per instance
//--------------------------------------------------------------------------------
Ogre::SkeletonInstance *skeletonInstance = m_logicSkeletonAnimManager->createSkeletonInstance( definition->m_skeletonDef, 1u );

//--------------------------------------------------------------------------------
// At Logic manager level
//--------------------------------------------------------------------------------
void LogicSystem::updateAllAnimationsThread( size_t threadIdx )
{
	using namespace Ogre;
	SkeletonAnimManager::BySkeletonDefList::iterator itByDef =
		m_skeletonAnimationManager->bySkeletonDefs.begin();
	SkeletonAnimManager::BySkeletonDefList::iterator enByDef =
		m_skeletonAnimationManager->bySkeletonDefs.end();

while( itByDef != enByDef )
{
	FastArray<SkeletonInstance *>::iterator itor =
		itByDef->skeletons.begin() + itByDef->threadStarts[threadIdx];
	FastArray<SkeletonInstance *>::iterator end =
		itByDef->skeletons.begin() + itByDef->threadStarts[threadIdx + 1];
	while( itor != end )
	{
		( *itor )->update();
		++itor;
	}

	if( !itByDef->skeletons.empty() )
		updateAnimationTransforms( *itByDef, threadIdx );

	++itByDef;
}
}

//--------------------------------------------------------------------------------
// Every Logic frame
//--------------------------------------------------------------------------------
updateAllAnimationsThread( 0 ); // I skip threading completely

//--------------------------------------------------------------------------------
// Destroying the instance
//--------------------------------------------------------------------------------
m_skeletonAnimationManager->destroySkeletonInstance( skeletonInstance );

//--------------------------------------------------------------------------------
// Destroy the type
//--------------------------------------------------------------------------------
m_logicSkeletonAnimManager->removeSkeletonDef( it2->second->getSkeletonDef() );

//--------------------------------------------------------------------------------
// Destroy the manager
//--------------------------------------------------------------------------------
delete m_logicSkeletonAnimManager

Of course this consumes more RAM and more CPU resources (because now you update 2 independent skeleton copies), but any solution what you want to achieve would eventually consume a lot of CPU because what you need is the final transform of multiples bones, and that needs updating its whole parent hierarchy. By the time you notice you likely end up updating the whole skeleton anyway.

But I wouldn't worry about that consumption unless profiling shows to be a problem (you'd need to have A LOT of skeletons with a lot of bones).

The upside of this technique is that you only really depend on Ogre when loading originalV1Skeleton.
Everything else (m_logicSkeletonAnimManager, m_skeletonDef, skeletonInstance) are classes entirely ran by you and thus independent from Ogre in every way.

knn217
Halfling
Posts: 78
Joined: Wed Jan 25, 2023 9:04 am
x 5

Re: how to get each bone's location and rotation during animations?

Post by knn217 »

Thanks for the suggestion!
As I understand, your solution is creating a skeletondef for each animation frame. For example:
animation_1 has 3 frames so there will be 3 skeletondefs (skeletondef1, skeletondef2, skeletondef3)

And the downside of this technique is that we only need the Position of each bone, but skeletondef offers way more than that so it consumes alot more RAM and CPU resources than neccessary.

So the .skeleton file probably doesn't contain all the bone Positions like I thought it does.

Your solution gives me an idea that may reduce RAM and CPU resources usage tho:
_Create a separate project to load in the .skeleton file
_Put it in a scene at Positon (0.0, 0.0, 0.0) so that all the bone positions will be an offset to the reference point
_Go through all available animations and get each frame's bone position (only the significant bones, small bones for details shouldn't affect hit/hurtbox)
_Save the bone positions in a .text file
_Load the .text file during scene creation in the logic thread of my game

Basically the same as yours but I need to create .text file instead of skeletondefs
The downside is that it requires an external project to create the .text file, and I need to rerun this everytime I made a change in the animation to update the .text file

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5476
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1358

Re: how to get each bone's location and rotation during animations?

Post by dark_sylinc »

knn217 wrote: Tue Mar 07, 2023 4:30 am

Thanks for the suggestion!
As I understand, your solution is creating a skeletondef for each animation frame. For example:
animation_1 has 3 frames so there will be 3 skeletondefs (skeletondef1, skeletondef2, skeletondef3)

...no?

A SkeletonDef contains everything: All bones, all animations and all frames.

However from what you are saying, I realized I assumed something that may not be true: I assumed you needed frame interpolation, and that the Item (i.e. the owner of the skeleton) may have arbitrary rotation.

But if yours is a Street Fighter-style of game, then perhaps the Item always has no rotation and you only need keyframes. If that's the case, then it drastically reduces complexity.

knn217
Halfling
Posts: 78
Joined: Wed Jan 25, 2023 9:04 am
x 5

Re: how to get each bone's location and rotation during animations?

Post by knn217 »

I see, I'll try messing around with the code you gave me for now. This code still work even if my skeleton and mesh were exported as v2 right?

knn217
Halfling
Posts: 78
Joined: Wed Jan 25, 2023 9:04 am
x 5

Re: how to get each bone's location and rotation during animations?

Post by knn217 »

Sorry, I couldn't get your code to run but 1st I need to understand the basic of how to get all the bone positions at every single frame of an animation from Skeletondef. Theres only 2 way I can think of how Skeletondef works and how to get the bones positions:

1.Does Skeletondef contains all the bone positions at every single frame of an animation? And we need to access one of its objects? (Since I've already tried mBones and it only returns the base positions, I think it might be mAnimationDefs, mBindPose or mReverseBindPose but after going through their definitions it got really confusing, still noob...).

2.Does Skeletondef only have the position of the base pose + the vectors to reach the following poses? And to get the positions of other poses we need to apply all those vectors in to calculate the bone positions in every frame? (This is likely the case since it reduce the data size Skeletondef takes up).

If it's the 1st case, I will continue to try getting the bone positions for every frame through Skeletondef.
If it's the 2nd case then doing this using Skeletondef is probably too hard for me right now, so I will go with the method of using an external project to write the bone positions to a file, and then load it to my game's logic thread on startup.