I need to extract the transform for a certain bone at all key-frames in an animation sequence. After digging around a bit it seems like “UAnimSequence::GetBonePose” is the correct method to use. Is this correct? How would you use that? It wants a “FCompactPose” as first argument, what is that and how do I initialize it?
So I finally got around to verifying this. Here are my findings, but anyone feel free to add/correct any of this:
If you are looking at an UAnimSequence (whether you have the anim sequence as a UPARAMETER or playing it directly on a Blueprint (or anim blueprint)), then it’s fairly straight forward:
int BoneIdx = GetMesh()->GetBoneIndex(TEXT("root"));
if(BoneIdx != INDEX_NONE)
{
int32 const TrackIndex = MyAnimSequence->GetSkeleton()->GetAnimationTrackIndex(BoneIdx, MyAnimSequence);
MyAnimSequence->GetBoneTransform(BoneTrans, TrackIndex, DesiredTime, true);
}
If you’re looking an an AnimMontage, then it’s more difficult because one montage may have several sequences. If you’re looking for a value in one of the sequences (at a particular time):
FName SlotNodeName = "DefaultSlot";
int SlotIndex = -1;
for (int32 I = 0; I < MyMontage->SlotAnimTracks.Num(); ++I)
{
if (MyMontage->SlotAnimTracks[I].SlotName == SlotNodeName)
{
SlotIndex = I;
break;
}
}
if (SlotIndex >= 0)
{
FAnimSegment* Result = nullptr;
for (FAnimSegment& Segment : MyMontage->SlotAnimTracks[SlotIndex].AnimTrack.AnimSegments)
{
if (Segment.AnimStartTime <= DesiredTime && DesiredTime <= Segment.StartPos + Segment.GetLength())
{
Result = &Segment;
break;
}
}
if (Result != nullptr)
{
FTransform BoneTrans;
int BoneIdx = GetMesh()->GetBoneIndex(TEXT("root"));
UAnimSequence *AnimSeq = Cast<UAnimSequence>(Result->AnimReference);
int32 const TrackIndex = AnimSeq->GetSkeleton()->GetAnimationTrackIndex(BoneIdx, AnimSeq);
AnimSeq->GetBoneTransform(BoneTrans, TrackIndex, DesiredTime, true);
}
}
You’ll notice I’m making assumptions about what bone to use (“root” in my example) and didn’t error check that BoneIdx != INDEX_NONE. Also notice the part that finds the appropriate segment is basically a copy of a function that already exists. If you do:
FAnimSegment* Segment = MyMontage->SlotAnimTracks[SlotIndex].AnimTrack.GetSegmentAtTime(DesiredTime);
it should give the same result, except that you’ll get a link error if you add that call because that function is not part of ENGINE_API (if you’re working from source, you can fix that).
On a Montage you can also get a list of all the sequences used by calling:
TArray<UAnimSequence *> AnimSeqs;
MyMontage->GetAllAnimationSequencesReferred(AnimSeqs)
you can iterate looking for the right one and then get the bone transform from it.
If you’re working with a montage but you’re not looking for a specific animation sequence but rather the end result pose (however many sequences are included in the montage), then this is the way:
FName SlotNodeName = "DefaultSlot";
float DesiredAnimTime = 0.0f;
FCompactPose Pose;
Pose.SetBoneContainer(&GetMesh()->GetAnimInstance()->RequiredBones);
FBlendedCurve Curve;
Curve.InitFrom(GetMesh()->SkeletalMesh->Skeleton);
FAnimExtractContext ExtractionContext(DesiredAnimTime, false);
MyMontage->GetAnimationData(SlotNodeName)->GetAnimationPose(Pose, Curve, ExtractionContext);
FCompactPoseBoneIndex BoneIndex(0);
RetVec = Pose[BoneIndex].GetTranslation(); // or GetRotation() or GetScale3D()
One hitch here is that “GetAnimationData” and “GetAnimationPose” are not part of ENGINE_API (so you’ll get unresolved symbol errors), which you can fix if you’re working from source, but if you’re not, you can make your own local version of those functions (actually GetAnimationPose calls another GetAnimationData part of the FAnimSegment class which you’ll also need to replicate).
EDIT: All the code above give you the transform for the bone in bone space. If you need it in component space, then you have to multiply down the hierarchy until you get to the root. On the last piece of code, for example, FCompactPose has the index of the parent bone: Pose->GetParentBoneIndex()
You can get the parent on any of them using the same mechanic. On the first piece of code for a sequence, MyAnimSequence->GetBonePose will get you the pose that you can then use to get the parent.
Hi there, i was looking for something similar, where i want to extract an entire pose at an arbitrary time in a sequence. Since i have done next to nothing c++ i am gonna have to start all the way from scratch if i want to figure out how to do this, but wanted to ask if you know if this can somehow be done in a sort of plugin/functionlibrary way, so i don’t have to modify the actual engine itself to get these informations out.
In order for this to be workable in a practical manner, i imagine i would store the pose data in a datastructure of some sort, so whatever node would come of of this, should somehow be able to deliver that data or the entire datastructure so i can do some additional math on it.
Hope this makes sense and you can give me a bit of pointers.
Without modifying source, assuming you have a reference to the sequence in question (MyAnimSequence):
bool bUseRawDataOnly = false;
int BoneIdx = 0;
float AnimTime = 0.1f;
const TArray<FTrackToSkeletonMap> & TrackToSkeletonMap = bUseRawDataOnly ? MyAnimSequence->TrackToSkeletonMapTable : MyAnimSequence->CompressedTrackToSkeletonMapTable;
if ((TrackToSkeletonMap.Num() > 0) && (TrackToSkeletonMap[0].BoneTreeIndex == 0))
{
FTransform BoneTransform;
MyAnimSequence->GetBoneTransform(BoneTransform, BoneIdx, AnimTime, bUseRawDataOnly);
}
With this you’d have to iterate through all the bones in the skeleton yourself. I don’t know of a way to get the whole pose at once. Animations are stored compressed and raw, but even the raw version is stored per bone and key so you don’t get a whole pose at once for an arbitrary anim time.
Hi rantrod, and thanks for the snippit. It’s perfectly ok and expected that i will need to get the pose, by extracting the key values pr. bone. So do you also know how this piece of code could be implemented so it can be exposed as a blueprint node or animation node somehow? I do realize this is a quite big question to ask, so if you by any chance know of a link to somewhere to start out learning to do this stuff, that will be greatly appreciated too.
What i try to accomplish with this, is to extract two poses (key values for all bones), and then somehow generate a dynamic animation consisting of interpolated keys based on an existing animation, and somehow expose this as an animationnode. The idea is to generate an adjustment blend animation on the run, that can be applied as an additive animation. So the animationnode should have a input taking an existing animation + two poses (start and end pose) and then deliver the additive animation as output. Hope this makes sense?
You can stick that piece of code pretty much anywhere and expose it to blueprints:
in the .h file (in a ‘public’ section) of the class you want to put this in:
UFUNCTION(BlueprintCallable, Category = "AnimData")
FTransform GetBoneTransformAtTime(UAnimSequence* MyAnimSequence, float AnimTime, int BoneIdx, bool bUseRawDataOnly);
in your .cpp file:
FTransform YourClass::GetBoneTransformAtTime(UAnimSequence* MyAnimSequence, float AnimTime, int BoneIdx, bool bUseRawDataOnly)
{
const TArray<FTrackToSkeletonMap> & TrackToSkeletonMap = bUseRawDataOnly ? MyAnimSequence->TrackToSkeletonMapTable : MyAnimSequence->CompressedTrackToSkeletonMapTable;
if ((TrackToSkeletonMap.Num() > 0) && (TrackToSkeletonMap[0].BoneTreeIndex == 0))
{
return MyAnimSequence->GetBoneTransform(BoneTransform, BoneIdx, AnimTime, bUseRawDataOnly);
}
return FTransform::Identity;
}
You can put it in an actor class, but you’d want to keep it generic so if your project has a ‘GameplayStatics’, it’s better there (you can create your own deriving from the Engine’s version).
Hey guys thank for posting all this info. I needed to be able to get the pose from an animation a while back, and this is what I have been using in the past:
Apologies for taking screen shot, pasting in the code took me out of character limit. This was working fine in 4.11, but my suspicion is it has been causing memory stomp issues since 4.12, probably since their multithreading anim evaluation changes. I am probably going to use the GetBoneTransform api for now since it appears to be safer. Going to pop in on UDN to see how I can fix the pose implementation, since I think using GetAnimationPose is still probably a better way in the end, since this is closer to what the actual animation evaluation uses. Also if you want to get the entire pose, it just makes more sense.