// Copyright Epic Games, Inc. All Rights Reserved. #include "PICO_HandTrackingLiveLinkRemapAsset.h" #include "PICO_HandTracking.h" #include "BonePose.h" #include "Roles/LiveLinkAnimationTypes.h" #define LOCTEXT_NAMESPACE "HandTrackingLiveLinkRemapAssetPICO" UHandTrackingLiveLinkRemapAssetPICO::UHandTrackingLiveLinkRemapAssetPICO(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } #if WITH_EDITORONLY_DATA void UHandTrackingLiveLinkRemapAssetPICO::PostInitProperties() { Super::PostInitProperties(); if (HandTrackingBoneNameMap.Num() == 0) { // Fill the first time use of the bone name map const UEnum* EnumPtr = FindObject(nullptr, TEXT("/Script/HeadMountedDisplay.EHandKeypoint"), true); check(EnumPtr != nullptr); // Iterate through all of the Keypoints building the skeleton info for it for (int32 Keypoint = 0; Keypoint < EHandKeypointCount; Keypoint++) { FName BoneName = FHandTrackingPICO::ParseEOpenXRHandKeypointEnumName(EnumPtr->GetNameByValue(Keypoint)); HandTrackingBoneNameMap.Add(BoneName, BoneName); } } } #endif void UHandTrackingLiveLinkRemapAssetPICO::BuildPoseFromAnimationData(float DeltaTime, const FLiveLinkSkeletonStaticData* InSkeletonData, const FLiveLinkAnimationFrameData* InFrameData, FCompactPose& OutPose) { check(InSkeletonData); check(InFrameData); // Transform Bone Names const TArray& SourceBoneNames = InSkeletonData->GetBoneNames(); TArray> TransformedBoneNames; TransformedBoneNames.Reserve(SourceBoneNames.Num()); for (const FName& SrcBoneName : SourceBoneNames) { TransformedBoneNames.Add(GetRemappedBoneName(SrcBoneName)); } for (int32 i = 0; i < TransformedBoneNames.Num(); ++i) { FName BoneName = TransformedBoneNames[i]; FTransform BoneTransform = GetRetargetedTransform(InFrameData, i); int32 MeshIndex = OutPose.GetBoneContainer().GetPoseBoneIndexForBoneName(BoneName); if (MeshIndex != INDEX_NONE) { FCompactPoseBoneIndex CPIndex = OutPose.GetBoneContainer().MakeCompactPoseIndex(FMeshPoseBoneIndex(MeshIndex)); if (CPIndex != INDEX_NONE) { FQuat OldRot = BoneTransform.GetRotation(); FQuat NewRot; FVector4 OldRotVector(OldRot.X, OldRot.Y, OldRot.Z, OldRot.W); NewRot.X = Sign(SwizzleX) * OldRotVector[StaticCast(SwizzleX) % 4]; NewRot.Y = Sign(SwizzleY) * OldRotVector[StaticCast(SwizzleY) % 4]; NewRot.Z = Sign(SwizzleZ) * OldRotVector[StaticCast(SwizzleZ) % 4]; NewRot.W = Sign(SwizzleW) * OldRotVector[StaticCast(SwizzleW) % 4]; // Left hand bones need to be flipped const FString* Handedness = InFrameData->MetaData.StringMetaData.Find(TEXT("Handedness")); if (Handedness && Handedness->Equals(TEXT("Left"))) { NewRot.X = -NewRot.X; NewRot.Y = -NewRot.Y; } NewRot.Normalize(); if (bRetargetRotationOnly) { // Ref pose rotated by the live link transform OutPose[CPIndex] = OutPose.GetRefPose(CPIndex); } else { OutPose[CPIndex] = BoneTransform; } OutPose[CPIndex].SetRotation(NewRot); } } } } FName UHandTrackingLiveLinkRemapAssetPICO::GetRemappedBoneName(FName BoneName) const { // Return the mapped name if we have one, otherwise just pass back the base name const FName* OutName = HandTrackingBoneNameMap.Find(BoneName); if (OutName != nullptr) { return *OutName; } return BoneName; } FTransform UHandTrackingLiveLinkRemapAssetPICO::GetRetargetedTransform(const FLiveLinkAnimationFrameData* InFrameData, int TransformIndex) const { check(InFrameData); FTransform OutTransform = InFrameData->Transforms[TransformIndex]; if (!bHasMetacarpals && ( TransformIndex == static_cast(EHandKeypoint::ThumbProximal) || TransformIndex == static_cast(EHandKeypoint::IndexProximal) || TransformIndex == static_cast(EHandKeypoint::MiddleProximal) || TransformIndex == static_cast(EHandKeypoint::RingProximal) || TransformIndex == static_cast(EHandKeypoint::LittleProximal) )) { // Metacarpal is always one entry before the Proximal OutTransform = InFrameData->Transforms[TransformIndex - 1] * OutTransform; } return OutTransform; } #undef LOCTEXT_NAMESPACE int Sign(const EQuatSwizzleAxisBPICO& QuatSwizzleAxis) { return (QuatSwizzleAxis > EQuatSwizzleAxisBPICO::W) ? -1 : 1; }