I answered this myself already - I’m posting it in case somebody might arrive here from a web search on a similar problem. I was manipulating bones through the interface in UPoseableMeshComponent. Many of the functions give you a EBoneSpaces::Type container, but the only choice for it are world space, and component space. There’s no bone space, which to me is the most common thing to want!
I did not find any solution in the documentation, and didn’t turn up much on this site either. After scouring the UE4 source code, I came up with this, to arrive at a component-relative bone transform, which you can use as the basis for further bone-relative manipulations. It only handles rotation, but you could extend it to translation easily. There could be a better way to do this, but if so, I never found it.
So here you go, future google-arriving person:
FQuat boneToComponent(FQuat::Identity);
if (Mesh != nullptr && Mesh->SkeletalMesh != nullptr) {
uint32 boneidx = Mesh->GetBoneIndex(BoneName);
const FReferenceSkeleton& refskel(Mesh->SkeletalMesh->RefSkeleton);
while (boneidx != INDEX_NONE) {
boneToComponent = refskel.GetRefBonePose()[boneidx].GetRotation() * boneToComponent;
boneidx = refskel.GetParentIndex(boneidx);
}
}
You can then quat-mul on your own transforms and set the bone’s new position, which you have to do by getting a rotator out of the quaternion, since there’s no SetBoneQuaternionByName:
const FQuat localRot = boneToComponent * my_quaternion;
Mesh->SetBoneRotationByName(BoneName, localRot.Rotator(), EBoneSpaces::ComponentSpace);