Get bone transforms from AnimBP, NOT from Skeletal Mesh

Hi,

The attached picture explains it all. I have a character with a mesh and an AnimBP. The AnimBP puts something into the final animation node and the character mesh will play that animation.

But now I want to have something like the GetSocketTransform node that gives me the bone transforms (in bone or component space) before they are applied to the skeletal mesh.

The reason is that I have some IK for which the effectors depend on the current animation that is supposed to play if there wasn’t any IK.

In short: How do I get bone transforms from the AnimBP alone without any reference to the spawned character?

One very ugly solution to this is to have two mesh components in your character. One invisible mesh that serves as a reference and only plays the regular AnimBP and the other one which is visible and plays the AnimBP with the additional IK. However before I go that far I want to make sure if there isn’t any other way (Blueprint or C++).

1 Like

I did quite some digging into the source code and I’m under the impression that it is not possible. However one could modify the source code. The AnimBP is evaluated in the skeletal mesh component. One could catch the bone transforms before the post process AnimBP (can be added to the mesh) is evaluated and then use the post process AnimBP to modify the animation based on the bone transforms coming from the regular AnimBP.

I will test this and post it as an answer if I succeed. It still isn’t an easy solution but at least it won’t include any nasty workarounds.

Hello, i am trying to deteriorate animation quality by applying inverse kinematics to the end bones and removing every other bit of information from the animation.
Since you didn’t reply to this post i am guessing what you tried didn’t work ?

Setting a variable directly somewhere inside the animation graph is impossible as far as I know. This is because the nodes you see on the graph are actually special classes, not functions on the animation instance class. If you want to access the transform of a bone at a specific point in the anim graph you need to create a custom anim graph node. The class you want to look at (or a struct actually) is FAnimNode_SkeletalControlBase. As an example you can look at the implementation of nodes such as FAnimNode_CopyBone or FAnimNode_ModifyBone. In there there are 3 main functions: InitializeBoneReferences, IsValidToEvaluate and EvaluateSkeletalControl_AnyThread. Inside the initialize function you want to set the bone references. In the copy bone implementation it looks like this:
// AnimNode_CopyBone.h

	UPROPERTY(EditAnywhere, Category = Copy)
	FBoneReference SourceBone;

	UPROPERTY(EditAnywhere, Category=Copy) 
	FBoneReference TargetBone;

// AnimNode_CopyBone.cpp

	SourceBone.Initialize(RequiredBones);
	TargetBone.Initialize(RequiredBones);

In the IsValidToEvaluate function you check whether those references are valid. Again, here is the example from the copy bone:
// AnimNode_CopyBone.cpp

	return (TargetBone.IsValidToEvaluate(RequiredBones) && (TargetBone==SourceBone || SourceBone.IsValidToEvaluate(RequiredBones)));

EvaluateSkeletalControl_AnyThread is the function where all the logic happens. Here is the simplified version of what you need to do to access the bone transforms:

void FAnimNode_CustomNode::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms)
{
	const FBoneContainer& BoneContainer = Output.Pose.GetPose().GetBoneContainer();

	FCompactPoseBoneIndex BoneIndex = BoneRef.GetCompactPoseIndex(BoneContainer);
	FTransform BoneTransform = Output.Pose.GetComponentSpaceTransform(BoneIndex);

where BoneRef is the FBoneReference variable defined in the header file.
To change the bone transform you do something like this:

	OutBoneTransforms.Add(FBoneTransform(BoneIndex, SomeTransform));

where BoneIndex is the index of the bone you want to modify.
You can add more variables to the node, if you give them the EditAnywhere specifier they will be visible in blueprints. You can also add meta=(PinShownByDefault) specifier to make them be exposed as a pin for the node.

You can do whatever you want with that transform in this function but again, you cannot access that value anywhere outside this function. If you need to do so, you either have to refactor your code so the whole logic that requires this variable happens inside this node or, as a dirty workaround, you can add a bone to your skeleton and set its transform to the transform you calculated here.
HOWEVER, that is not the end. You have the node logic pretty much complete but to actually make it usable in the graph, you need to create a subclass of class UAnimGraphNode_SkeletalControlBase. The class for your node should look like this:

// .h
UCLASS(MinimalAPI)
class UAnimGraphNode_CustomNode : public UAnimGraphNode_SkeletalControlBase
{
	GENERATED_UCLASS_BODY()

	UPROPERTY(EditAnywhere, Category=Settings)
	FAnimNode_YourNode Node;

	virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
	virtual FText GetTooltipText() const override;
// .cpp
#define LOCTEXT_NAMESPACE "A3Nodes"

UAnimGraphNode_CustomNode::UAnimGraphNode_CustomNode(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

FText UAnimGraphNode_CustomNode::GetTooltipText() const
{
	return LOCTEXT("AnimGraphNode_YourNode_Tooltip", "Tooltip");
}

FText UAnimGraphNode_YourNode::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
	return "YourNodeName";
}

#undef LOCTEXT_NAMESPACE

In the GetNodeTitle function you can put way more stuff to make your node look fancy, for an example you can check the AnimGraphNode_CopyBone.cpp file. After you do all that you need to add a few dependencies to your .build.cs file:

PublicDependecyModuleNames.AddRange(new string[] { "AnimGraphRuntime", "AnimGraph", "BlueprintGraph", "Kismet", "KismetCompiler", "UnrealEd" })

All this should allow you to add your node to the animation graph and use it. OP has probably already moved on with his life but I hope that others that just like me came across this post 7 years later will find it useful :slight_smile:

2 Likes