Download

Bone chain transform problem

Hello everyone,
I’m scratching my head with some unexpected behaviour I’m getting with multiplying the bone transforms of a chain as follows:

FTransform WeaponToHandTransform =
	FirearmMesh->GetSocketTransform(FName("recoil_space"), ERelativeTransformSpace::RTS_Component) *
	CharacterMesh->GetSocketTransform(AttachSocket, ERelativeTransformSpace::RTS_ParentBoneSpace);
	

FTransform RecoilTransform = FTransform(RecoilSpaceRotation.Quaternion());

return (WeaponToHandTransform.Inverse() * RecoilTransform * WeaponToHandTransform).Rotator();

This yields the expected result. A rotator representing an equivalent rotation in RecoilSpace (defined by the socket named “recoil_space”) in hand space.

When I tried doing the same thing but to obtain the equivalent rotation in upperarm space, like this:

FTransform LowerArmTransform;
UAnimUtils::GetBoneTransformFromSequence(LowerArmTransform, FName("lowerarm_r"), CharacterMesh, BaseSequence, 0.0f, ERelativeTransformSpace::RTS_ParentBoneSpace);
FTransform HandTransform;
UAnimUtils::GetBoneTransformFromSequence(HandTransform, FName("hand_r"), CharacterMesh, BaseSequence, 0.0f, ERelativeTransformSpace::RTS_ParentBoneSpace);

FTransform WeaponToUpperArmTransform =
	FirearmMesh->GetSocketTransform(FName("recoil_space"), ERelativeTransformSpace::RTS_Component) *
	CharacterMesh->GetSocketTransform(AttachSocket, ERelativeTransformSpace::RTS_ParentBoneSpace) *
	HandTransform *
	LowerArmTransform;

FTransform RecoilTransform = FTransform(RecoilSpaceRotation.Quaternion());

return (WeaponToUpperArmTransform.Inverse() * RecoilTransform * WeaponToUpperArmTransform).Rotator();

NOTE: The function GetBoneTransformFromSequence returns the bone transform from a specific sequence in the specified space. (I’ve used it many times in the past and it seems to work fine). I will add the code at the end just in case.

The problem is that, following the same reasoning and matrix multiplication order as with the hand, the result is different, it seems to rotate around a different axis but if instead I return the following, it seems to work.

return (WeaponToUpperArmTransform * RecoilTransform * WeaponToUpperArmTransform.Inverse()).Rotator().GetInverse();

Here is the code for the GetBoneTransformFromSequence function:

void UAnimUtils::GetBoneTransformFromSequence(FTransform& Out, FName BoneName, USkeletalMeshComponent* SkeletalMeshComponent, UAnimSequence* Sequence, float Time, ERelativeTransformSpace Space)
{
int32 BoneIndex = SkeletalMeshComponent->GetBoneIndex(BoneName);
if (BoneIndex == INDEX_NONE)
{
	Out = FTransform::Identity;
	return;
}

const TArray<FTrackToSkeletonMap> & TrackToSkeletonMap = Sequence->GetCompressedTrackToSkeletonMapTable();
if (TrackToSkeletonMap.Num() == 0 || TrackToSkeletonMap[0].BoneTreeIndex != 0)
{
	Out = FTransform::Identity;
	return;
}

Sequence->GetBoneTransform(Out, BoneIndex, Time, false);

// If desired space is not bone space then go up to component
if (Space != ERelativeTransformSpace::RTS_ParentBoneSpace)
{
	int32 ParentBoneIndex;
	FTransform Transform;
	FName ParentBoneName = SkeletalMeshComponent->GetParentBone(BoneName);

	while (ParentBoneName != NAME_None)
	{
		ParentBoneIndex = SkeletalMeshComponent->GetBoneIndex(ParentBoneName);
		Sequence->GetBoneTransform(Transform, ParentBoneIndex, Time, false);
		Out *= Transform;
		ParentBoneName = SkeletalMeshComponent->GetParentBone(ParentBoneName);
	}
}

// Now Out is relative to ComponentSpace
if (Space != ERelativeTransformSpace::RTS_Component)
{
	if (Space == ERelativeTransformSpace::RTS_Actor)
	{
		Out *= SkeletalMeshComponent->GetRelativeTransform();
	}
	else
	{
		Out *= SkeletalMeshComponent->GetComponentTransform();
	}
}
}

I would like to know why this is the case. What am I doing wrong?