Switch to VRExpansionPlugin Example 2

This commit is contained in:
Simeon "Waldo" Wallrath 2023-11-03 13:45:36 +01:00
parent 3e6ff514ff
commit 6c02da9299
837 changed files with 78100 additions and 0 deletions

View file

@ -0,0 +1,8 @@
[FilterPlugin]
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
;
; Examples:
; /README.txt
; /Extras/...
; /Binaries/ThirdParty/*.dll

View file

@ -0,0 +1,45 @@
{
"FileVersion": 3,
"Version": 5.3,
"VersionName": "5.3",
"FriendlyName": "OpenXRExpansionPlugin",
"Description": "An set of utility functions for OpenXR",
"Category": "Virtual Reality",
"CreatedBy": "Joshua (MordenTral) Statzer",
"CreatedByURL": "http://www.vreue4.com",
"DocsURL": "http://www.vreue4.com",
"MarketplaceURL": "",
"SupportURL": "http://www.vreue4.com",
"EnabledByDefault": false,
"CanContainContent": false,
"IsBetaVersion": false,
"Installed": true,
"SupportedTargetPlatforms": [
"Win64",
"Linux",
"Android"
],
"Modules": [
{
"Name": "OpenXRExpansionPlugin",
"Type": "Runtime",
"LoadingPhase": "PreDefault"
}
,
{
"Name": "OpenXRExpansionEditor",
"Type": "UnCookedOnly",
"LoadingPhase": "PostEngineInit"
}
],
"Plugins": [
{
"Name": "OpenXR",
"Enabled": true
},
{
"Name": "XRBase",
"Enabled": true
}
]
}

View file

@ -0,0 +1,61 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
using System.IO;
namespace UnrealBuildTool.Rules
{
public class OpenXRExpansionEditor : ModuleRules
{
public OpenXRExpansionEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
// ... add other public dependencies that you statically link with here ...
"Engine",
"Core",
"CoreUObject"
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"UnrealEd",
"BlueprintGraph",
"AnimGraph",
"AnimGraphRuntime",
"SlateCore",
"Slate",
"InputCore",
"Engine",
"EditorStyle",
"AssetRegistry",
"OpenXRExpansionPlugin"
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
}

View file

@ -0,0 +1,34 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "AnimGraphNode_ApplyOpenXRHandPose.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimGraphNode_ApplyOpenXRHandPose)
/////////////////////////////////////////////////////
// UAnimGraphNode_ModifyBSHand
UAnimGraphNode_ApplyOpenXRHandPose::UAnimGraphNode_ApplyOpenXRHandPose(const FObjectInitializer& Initializer)
: Super(Initializer)
{
}
//Title Color!
FLinearColor UAnimGraphNode_ApplyOpenXRHandPose::GetNodeTitleColor() const
{
return FLinearColor(12, 12, 0, 1);
}
//Node Category
FString UAnimGraphNode_ApplyOpenXRHandPose::GetNodeCategory() const
{
return FString("OpenXR");
}
FText UAnimGraphNode_ApplyOpenXRHandPose::GetControllerDescription() const
{
return FText::FromString("Apply OpenXR Hand Pose");
}
FText UAnimGraphNode_ApplyOpenXRHandPose::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
FText Result = GetControllerDescription();
return Result;
}

View file

@ -0,0 +1,16 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "OpenXRExpansionEditor.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
IMPLEMENT_MODULE(FOpenXRExpansionEditorModule, OpenXRExpansionEditor);
void FOpenXRExpansionEditorModule::StartupModule()
{
}
void FOpenXRExpansionEditorModule::ShutdownModule()
{
}

View file

@ -0,0 +1,34 @@
#pragma once
#include "AnimGraphDefinitions.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Editor/AnimGraph/Public/AnimGraphNode_SkeletalControlBase.h"
#include "AnimNode_ApplyOpenXRHandPose.h"
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "AnimGraphNode_ApplyOpenXRHandPose.generated.h"
UCLASS(MinimalAPI)
class UAnimGraphNode_ApplyOpenXRHandPose : public UAnimGraphNode_SkeletalControlBase
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, Category = Settings)
FAnimNode_ApplyOpenXRHandPose Node;
public:
// UEdGraphNode interface
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
virtual FLinearColor GetNodeTitleColor() const override;
virtual FString GetNodeCategory() const override;
// End of UEdGraphNode interface
protected:
// UAnimGraphNode_SkeletalControlBase protected interface
virtual FText GetControllerDescription() const;
virtual const FAnimNode_SkeletalControlBase* GetNode() const override { return &Node; }
// End of UAnimGraphNode_SkeletalControlBase protected interface
};

View file

@ -0,0 +1,13 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Runtime/Core/Public/Modules/ModuleInterface.h"
class FOpenXRExpansionEditorModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View file

@ -0,0 +1,70 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.IO;
namespace UnrealBuildTool.Rules
{
public class OpenXRExpansionPlugin: ModuleRules
{
public OpenXRExpansionPlugin(ReadOnlyTargetRules Target)
: base(Target)
{
PublicDependencyModuleNames.AddRange(
new string[]
{
//"InputDevice",
//"LiveLink",
//"LiveLinkInterface"
}
);
var EngineDir = Path.GetFullPath(Target.RelativeEnginePath);
PrivateIncludePaths.AddRange(
new string[] {
EngineDir + "Plugins/Runtime/OpenXR/Source/OpenXRHMD/Private",
EngineDir + "/Source/ThirdParty/OpenXR/include",
// ... add other private include paths required here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"NetCore",
"CoreUObject",
//"ApplicationCore",
"Engine",
//"InputDevice",
"InputCore",
"Slate",
"HeadMountedDisplay",
//"AnimGraph",
"AnimGraphRuntime",
"SlateCore",
"XRBase"
//"LiveLink",
//"LiveLinkInterface",
}
);
if (Target.Platform != UnrealTargetPlatform.Mac)
{
PrivateDependencyModuleNames.AddRange(
new string[]
{
"OpenXRHMD"
}
);
PrivateDefinitions.AddRange(new string[] { "OPENXR_SUPPORTED" });
AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenXR");
}
// if (Target.bBuildEditor == true)
// {
// PrivateDependencyModuleNames.Add("UnrealEd");
// }
}
}
}

View file

@ -0,0 +1,463 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AnimNode_ApplyOpenXRHandPose.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNode_ApplyOpenXRHandPose)
//#include "EngineMinimal.h"
//#include "Engine/Engine.h"
//#include "CoreMinimal.h"
#include "OpenXRExpansionFunctionLibrary.h"
#include "AnimNode_ApplyOpenXRHandPose.h"
#include "AnimationRuntime.h"
#include "DrawDebugHelpers.h"
#include "OpenXRHandPoseComponent.h"
#include "Runtime/Engine/Public/Animation/AnimInstanceProxy.h"
#include "BoneControllers/AnimNode_SkeletalControlBase.h"
FAnimNode_ApplyOpenXRHandPose::FAnimNode_ApplyOpenXRHandPose()
: FAnimNode_SkeletalControlBase()
{
WorldIsGame = false;
Alpha = 1.f;
SkeletonType = EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Right;
bIsOpenInputAnimationInstance = false;
bSkipRootBone = false;
bOnlyApplyWristTransform = false;
//WristAdjustment = FQuat::Identity;
}
void FAnimNode_ApplyOpenXRHandPose::OnInitializeAnimInstance(const FAnimInstanceProxy* InProxy, const UAnimInstance* InAnimInstance)
{
Super::OnInitializeAnimInstance(InProxy, InAnimInstance);
if (const UOpenXRAnimInstance * animInst = Cast<UOpenXRAnimInstance>(InAnimInstance))
{
bIsOpenInputAnimationInstance = true;
}
}
void FAnimNode_ApplyOpenXRHandPose::Initialize_AnyThread(const FAnimationInitializeContext& Context)
{
Super::Initialize_AnyThread(Context);
}
void FAnimNode_ApplyOpenXRHandPose::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)
{
Super::CacheBones_AnyThread(Context);
}
void FAnimNode_ApplyOpenXRHandPose::InitializeBoneReferences(const FBoneContainer& RequiredBones)
{
UObject* OwningAsset = RequiredBones.GetAsset();
if (!OwningAsset)
return;
USkeleton* AssetSkeleton = RequiredBones.GetSkeletonAsset();
if (!AssetSkeleton)
return;
if (!MappedBonePairs.bInitialized || OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton)
{
// Trigger a full re-build if our asset changed
if (MappedBonePairs.bInitialized && (OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton))
{
MappedBonePairs.ClearMapping();
}
MappedBonePairs.LastInitializedName = OwningAsset->GetFName();
MappedBonePairs.LastInitializedSkeleton = SkeletonType;
MappedBonePairs.bInitialized = false;
if (AssetSkeleton)
{
// If our bone pairs are empty, then setup our sane defaults
if (!MappedBonePairs.BonePairs.Num())
{
MappedBonePairs.ConstructDefaultMappings(SkeletonType, bSkipRootBone);
}
// Construct a reverse map of our joints
MappedBonePairs.ConstructReverseMapping();
TArray<FTransform> RefBones = AssetSkeleton->GetReferenceSkeleton().GetRefBonePose();
TArray<FMeshBoneInfo> RefBonesInfo = AssetSkeleton->GetReferenceSkeleton().GetRefBoneInfo();
for (FBPOpenXRSkeletalPair& BonePair : MappedBonePairs.BonePairs)
{
// Fill in the bone name for the reference
BonePair.ReferenceToConstruct.BoneName = BonePair.BoneToTarget;
// Init the reference
BonePair.ReferenceToConstruct.Initialize(AssetSkeleton);
BonePair.ReferenceToConstruct.CachedCompactPoseIndex = BonePair.ReferenceToConstruct.GetCompactPoseIndex(RequiredBones);
if ((BonePair.ReferenceToConstruct.CachedCompactPoseIndex != INDEX_NONE))
{
// Get our parent bones index
BonePair.ParentReference = RequiredBones.GetParentBoneIndex(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
}
}
MappedBonePairs.bInitialized = true;
if (SkeletonType == EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left || SkeletonType == EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Right)
{
// We hard code this for now because I don't like their wrist being a different transform
MappedBonePairs.AdjustmentQuat = FRotator(0.f, 90.f, 180.f).Quaternion(); // Current one is incorrect without wrist
// Maybe do it in relative space?
}
else
{
CalculateSkeletalAdjustment(AssetSkeleton);
}
}
}
}
void FAnimNode_ApplyOpenXRHandPose::CalculateSkeletalAdjustment(USkeleton* AssetSkeleton)
{
TArray<FTransform> RefBones = AssetSkeleton->GetReferenceSkeleton().GetRefBonePose();
TArray<FMeshBoneInfo> RefBonesInfo = AssetSkeleton->GetReferenceSkeleton().GetRefBoneInfo();
if (!MappedBonePairs.bInitialized || MappedBonePairs.BonePairs.Num() < 4 || !RefBones.Num())
{
UE_LOG(LogTemp, Error, TEXT("Empty or incorrect mapping or skeleton data when calculating skeletal adjustment!"));
return;
}
FBPOpenXRSkeletalPair KnuckleIndexPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair KnuckleMiddlePair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair KnuckleRingPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair KnucklePinkyPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair WristPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT]];
FVector KnuckleAverage = GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleIndexPair.ReferenceToConstruct.BoneIndex).GetTranslation();
KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleMiddlePair.ReferenceToConstruct.BoneIndex).GetTranslation();
KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleRingPair.ReferenceToConstruct.BoneIndex).GetTranslation();
KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnucklePinkyPair.ReferenceToConstruct.BoneIndex).GetTranslation();
// Get our average across the knuckles
KnuckleAverage /= 4.f;
// Obtain the UE4 wrist Side & Forward directions from first animation frame and place in cache
FTransform WristTransform_UE = GetRefBoneInCS(RefBones, RefBonesInfo, WristPair.ReferenceToConstruct.BoneIndex);
FVector ToKnuckleAverage_UE = KnuckleAverage - WristTransform_UE.GetTranslation();
ToKnuckleAverage_UE.Normalize();
WristForwardLS_UE = WristTransform_UE.GetRotation().UnrotateVector(ToKnuckleAverage_UE);
SetVectorToMaxElement(WristForwardLS_UE);
WristSideDirectionLS = FVector::CrossProduct(WristForwardLS_UE, FVector::RightVector);
SetVectorToMaxElement(WristSideDirectionLS);
CalculateOpenXRAdjustment();
}
void FAnimNode_ApplyOpenXRHandPose::CalculateOpenXRAdjustment()
{
// Base implementation is like valves
// Forward direction
static FVector OpenXRForwardDirection = FVector(1.0f, 0.f, 0.f);
// Side direction
// Do I need to flip this for left hand?
static FVector OpenXRSideDirection = FVector(0.f, 1.f, 0.f);
// Align forward vectors, openXR once in engine is X+ forward
FQuat AlignmentRot = FQuat::FindBetweenNormals(WristForwardLS_UE, OpenXRForwardDirection);
// Rotate about the aligned forward direction to make the side directions align
FVector WristSideDirectionMS_UE = AlignmentRot * WristSideDirectionLS;
// Rotate around, should the Side direction flip for openXR if its the left hand?
FQuat TwistRotation = CalcRotationAboutAxis(WristSideDirectionMS_UE, OpenXRSideDirection, OpenXRForwardDirection);
FRotator Difference = (TwistRotation * AlignmentRot).Rotator();
MappedBonePairs.AdjustmentQuat = (TwistRotation * AlignmentRot).GetNormalized();
}
void FAnimNode_ApplyOpenXRHandPose::ConvertHandTransformsSpace(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms, FTransform AddTrans, bool bMirrorLeftRight, bool bMergeMissingUE4Bones)
{
// Fail if the count is too low
if (WorldTransforms.Num() < EHandKeypointCount)
return;
if (OutTransforms.Num() < WorldTransforms.Num())
{
OutTransforms.Empty(WorldTransforms.Num());
OutTransforms.AddUninitialized(WorldTransforms.Num());
}
TArray<FTransform> TempWorldTransforms = WorldTransforms;
// Ensure add trans is normalized
AddTrans.NormalizeRotation();
// Bone/Parent map
int32 BoneParents[26] =
{
// Manually build the parent hierarchy starting at the wrist which has no parent (-1)
1, // Palm -> Wrist
-1, // Wrist -> None
1, // ThumbMetacarpal -> Wrist
2, // ThumbProximal -> ThumbMetacarpal
3, // ThumbDistal -> ThumbProximal
4, // ThumbTip -> ThumbDistal
1, // IndexMetacarpal -> Wrist
6, // IndexProximal -> IndexMetacarpal
7, // IndexIntermediate -> IndexProximal
8, // IndexDistal -> IndexIntermediate
9, // IndexTip -> IndexDistal
1, // MiddleMetacarpal -> Wrist
11, // MiddleProximal -> MiddleMetacarpal
12, // MiddleIntermediate -> MiddleProximal
13, // MiddleDistal -> MiddleIntermediate
14, // MiddleTip -> MiddleDistal
1, // RingMetacarpal -> Wrist
16, // RingProximal -> RingMetacarpal
17, // RingIntermediate -> RingProximal
18, // RingDistal -> RingIntermediate
19, // RingTip -> RingDistal
1, // LittleMetacarpal -> Wrist
21, // LittleProximal -> LittleMetacarpal
22, // LittleIntermediate -> LittleProximal
23, // LittleDistal -> LittleIntermediate
24, // LittleTip -> LittleDistal
};
bool bUseAutoCalculatedRetarget = AddTrans.Equals(FTransform::Identity);
// Convert transforms to parent space
// The hand tracking transforms are in world space.
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
if (TempWorldTransforms[Index].ContainsNaN() || TempWorldTransforms[Index].Equals(FTransform::Identity))
{
OutTransforms[Index] = FTransform::Identity;
//continue;
}
// Ensure normalization
TempWorldTransforms[Index].NormalizeRotation();
if (bMirrorLeftRight)
{
TempWorldTransforms[Index].Mirror(EAxis::Y, EAxis::Y);
}
if (bUseAutoCalculatedRetarget)
{
TempWorldTransforms[Index].ConcatenateRotation(MappedBonePairs.AdjustmentQuat);
//WorldTransforms[Index].ConcatenateRotation(MappedBonePairs.BonePairs[0].RetargetRot);
}
else
{
TempWorldTransforms[Index].ConcatenateRotation(AddTrans.GetRotation());
}
}
// Make this into a single loop, their structure always has children after parent
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
FTransform& BoneTransform = TempWorldTransforms[Index];
//BoneTransform.NormalizeRotation();
int32 ParentIndex = BoneParents[Index];
int32 ParentParent = -1;
// Thumb keeps the metacarpal intact, we don't skip it
if (bMergeMissingUE4Bones)
{
if (Index != (int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT && ParentIndex > 0)
{
ParentParent = BoneParents[ParentIndex];
}
}
if (ParentIndex < 0)
{
// We are at the root, so use it.
OutTransforms[Index] = BoneTransform;
}
else
{
FTransform ParentTransform = FTransform::Identity;
// Merging missing metacarpal bone into the transform
if (bMergeMissingUE4Bones && ParentParent == 1) // Wrist
{
ParentTransform = TempWorldTransforms[ParentParent];
}
else
{
ParentTransform = TempWorldTransforms[ParentIndex];
}
//ParentTransform.NormalizeRotation();
OutTransforms[Index] = BoneTransform.GetRelativeTransform(ParentTransform);
}
}
}
void FAnimNode_ApplyOpenXRHandPose::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms)
{
if (!MappedBonePairs.bInitialized)
return;
const FBoneContainer& BoneContainer = Output.Pose.GetPose().GetBoneContainer();
UObject* OwningAsset = BoneContainer.GetAsset();
if (!OwningAsset)
return;
// Trigger a full re-build if our asset or target skeleton changed, do it up here before finding the correct hand
if ((OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton))
{
InitializeBoneReferences(BoneContainer);
}
/*const */FBPOpenXRActionSkeletalData *StoredActionInfoPtr = nullptr;
if (bIsOpenInputAnimationInstance)
{
/*const*/ FOpenXRAnimInstanceProxy* OpenXRAnimInstance = (FOpenXRAnimInstanceProxy*)Output.AnimInstanceProxy;
if (OpenXRAnimInstance->HandSkeletalActionData.Num())
{
for (int i = 0; i <OpenXRAnimInstance->HandSkeletalActionData.Num(); ++i)
{
EVRSkeletalHandIndex TargetHand = OpenXRAnimInstance->HandSkeletalActionData[i].TargetHand;
if (OpenXRAnimInstance->HandSkeletalActionData[i].bMirrorLeftRight)
{
TargetHand = (TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left) ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
}
if (TargetHand == MappedBonePairs.TargetHand)
{
StoredActionInfoPtr = &OpenXRAnimInstance->HandSkeletalActionData[i];
break;
}
}
}
}
// If we have an empty hand pose but have a passed in custom one then use that
if (StoredActionInfoPtr == nullptr || !StoredActionInfoPtr->SkeletalTransforms.Num())
{
StoredActionInfoPtr = &OptionalStoredActionInfo;
}
if (!StoredActionInfoPtr->bHasValidData)
{
return;
}
//MappedBonePairs.AdjustmentQuat = WristAdjustment;
// Currently not blending correctly
const float BlendWeight = FMath::Clamp<float>(ActualAlpha, 0.f, 1.f);
uint8 BoneTransIndex = 0;
uint8 NumBones = StoredActionInfoPtr ? StoredActionInfoPtr->SkeletalTransforms.Num() : 0;
if (NumBones < 1)
{
// Early out, we don't have a valid data to work with
return;
}
FTransform trans = FTransform::Identity;
OutBoneTransforms.Reserve(MappedBonePairs.BonePairs.Num());
TArray<FBoneTransform> TransBones;
FTransform AdditionTransform = StoredActionInfoPtr->AdditionTransform;
FTransform TempTrans = FTransform::Identity;
FTransform ParentTrans = FTransform::Identity;
FTransform * ParentTransPtr = nullptr;
//AdditionTransform.SetRotation(MappedBonePairs.AdjustmentQuat);
TArray<FTransform> HandTransforms;
ConvertHandTransformsSpace(HandTransforms, StoredActionInfoPtr->SkeletalTransforms, AdditionTransform, StoredActionInfoPtr->bMirrorLeftRight, MappedBonePairs.bMergeMissingBonesUE4);
for (const FBPOpenXRSkeletalPair& BonePair : MappedBonePairs.BonePairs)
{
BoneTransIndex = (int8)BonePair.OpenXRBone;
ParentTrans = FTransform::Identity;
if (bSkipRootBone && BonePair.OpenXRBone == EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT)
continue;
if (BoneTransIndex >= NumBones || BonePair.ReferenceToConstruct.CachedCompactPoseIndex == INDEX_NONE)
continue;
if (!BonePair.ReferenceToConstruct.IsValidToEvaluate(BoneContainer))
{
continue;
}
trans = Output.Pose.GetComponentSpaceTransform(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
if (BonePair.ParentReference != INDEX_NONE)
{
ParentTrans = Output.Pose.GetComponentSpaceTransform(BonePair.ParentReference);
ParentTrans.SetScale3D(FVector(1.f));
}
EXRHandJointType CurrentBone = (EXRHandJointType)BoneTransIndex;
TempTrans = (HandTransforms[BoneTransIndex]);
//TempTrans.ConcatenateRotation(BonePair.RetargetRot);
/*if (StoredActionInfoPtr->bMirrorHand)
{
FMatrix M = TempTrans.ToMatrixWithScale();
M.Mirror(EAxis::Z, EAxis::X);
M.Mirror(EAxis::X, EAxis::Z);
TempTrans.SetFromMatrix(M);
}*/
TempTrans = TempTrans * ParentTrans;
if (StoredActionInfoPtr->bAllowDeformingMesh || bOnlyApplyWristTransform)
trans.SetTranslation(TempTrans.GetTranslation());
trans.SetRotation(TempTrans.GetRotation());
TransBones.Add(FBoneTransform(BonePair.ReferenceToConstruct.CachedCompactPoseIndex, trans));
// Need to do it per bone so future bones are correct
// Only if in parent space though, can do it all at the end in component space
if (TransBones.Num())
{
Output.Pose.LocalBlendCSBoneTransforms(TransBones, BlendWeight);
TransBones.Reset();
}
if (bOnlyApplyWristTransform && CurrentBone == EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT)
{
break; // Early out of the loop, we only wanted to apply the wrist
}
}
}
bool FAnimNode_ApplyOpenXRHandPose::IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones)
{
return(/*MappedBonePairs.bInitialized && */MappedBonePairs.BonePairs.Num() > 0);
}

View file

@ -0,0 +1,587 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "OpenXRExpansionFunctionLibrary.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(OpenXRExpansionFunctionLibrary)
//#include "EngineMinimal.h"
#include "Engine/Engine.h"
#include <openxr/openxr.h>
#include "CoreMinimal.h"
#include "IXRTrackingSystem.h"
//General Log
DEFINE_LOG_CATEGORY(OpenXRExpansionFunctionLibraryLog);
UOpenXRExpansionFunctionLibrary::UOpenXRExpansionFunctionLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
//=============================================================================
UOpenXRExpansionFunctionLibrary::~UOpenXRExpansionFunctionLibrary()
{
}
void UOpenXRExpansionFunctionLibrary::GetXRMotionControllerType(FString& TrackingSystemName, EBPOpenXRControllerDeviceType& DeviceType, EBPXRResultSwitch& Result)
{
#if defined(OPENXR_SUPPORTED)
DeviceType = EBPOpenXRControllerDeviceType::DT_UnknownController;
Result = EBPXRResultSwitch::OnFailed;
if (FOpenXRHMD* pOpenXRHMD = GetOpenXRHMD())
{
XrInstance XRInstance = pOpenXRHMD->GetInstance();
XrSystemId XRSysID = pOpenXRHMD->GetSystem();
if (XRSysID && XRInstance)
{
XrSystemProperties systemProperties{ XR_TYPE_SYSTEM_PROPERTIES };
systemProperties.next = nullptr;
if (xrGetSystemProperties(XRInstance, XRSysID, &systemProperties) == XR_SUCCESS)
{
XrSession XRSesh = pOpenXRHMD->GetSession();
if (XRSesh)
{
XrPath myPath;
XrResult PathResult = xrStringToPath(XRInstance, "/user/hand/left", &myPath);
XrInteractionProfileState interactionProfile{ XR_TYPE_INTERACTION_PROFILE_STATE };
interactionProfile.next = nullptr;
XrResult QueryResult = xrGetCurrentInteractionProfile(XRSesh, myPath, &interactionProfile);
if (QueryResult == XR_SUCCESS)
{
char myPathy[XR_MAX_SYSTEM_NAME_SIZE];
uint32_t outputsize;
xrPathToString(XRInstance, interactionProfile.interactionProfile, XR_MAX_SYSTEM_NAME_SIZE, &outputsize, myPathy);
if (interactionProfile.interactionProfile == XR_NULL_PATH || outputsize < 1)
return;
FString InteractionName(ANSI_TO_TCHAR(myPathy));
if (InteractionName.Len() < 1)
return;
/*
* Interaction profile paths [6.4]
An interaction profile identifies a collection of buttons and
other input sources, and is of the form:
/interaction_profiles/<vendor_name>/<type_name>
Paths supported in the core 1.0 release
/interaction_profiles/khr/simple_control
/interaction_profiles/khr/simple_controller
/interaction_profiles/google/daydream_controller
/interaction_profiles/htc/vive_controller
/interaction_profiles/htc/vive_pro
/interaction_profiles/microsoft/motion_controller
/interaction_profiles/microsoft/xbox_controller
/interaction_profiles/oculus/go_controller
/interaction_profiles/oculus/touch_controller
/interaction_profiles/valve/index_controller
*/
// Not working currently?
/*XrInputSourceLocalizedNameGetInfo InputSourceInfo{ XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO };
InputSourceInfo.next = nullptr;
InputSourceInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT;
InputSourceInfo.sourcePath = interactionProfile.interactionProfile;
char buffer[XR_MAX_SYSTEM_NAME_SIZE];
uint32_t usedBufferCount = 0;
if (xrGetInputSourceLocalizedName(XRSesh, &InputSourceInfo, XR_MAX_SYSTEM_NAME_SIZE, &usedBufferCount, (char*)&buffer) == XR_SUCCESS)
{
int g = 0;
}*/
if (InteractionName.Find("touch_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_OculusTouchController;
}
else if (InteractionName.Contains("index_controller", ESearchCase::IgnoreCase))
{
DeviceType = EBPOpenXRControllerDeviceType::DT_ValveIndexController;
}
else if (InteractionName.Find("vive_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_ViveController;
}
else if (InteractionName.Find("vive_pro", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_ViveProController;
}
else if (InteractionName.Find("simple_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_SimpleController;
}
else if (InteractionName.Find("daydream_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_DaydreamController;
}
else if (InteractionName.Find("motion_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_MicrosoftMotionController;
}
else if (InteractionName.Find("xbox_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_MicrosoftXboxController;
}
else if (InteractionName.Find("go_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_OculusGoController;
}
else if (InteractionName.Find("neo3_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_PicoNeo3Controller;
}
else if (InteractionName.Find("mixed_reality_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_WMRController;
}
else
{
UE_LOG(OpenXRExpansionFunctionLibraryLog, Warning, TEXT("UNKNOWN OpenXR Interaction profile detected!!!: %s"), *InteractionName);
DeviceType = EBPOpenXRControllerDeviceType::DT_UnknownController;
}
Result = EBPXRResultSwitch::OnSucceeded;
}
TrackingSystemName = FString(ANSI_TO_TCHAR(systemProperties.systemName));// , XR_MAX_SYSTEM_NAME_SIZE);
//VendorID = systemProperties.vendorId;
return;
}
}
}
}
#endif
TrackingSystemName.Empty();
return;
}
bool UOpenXRExpansionFunctionLibrary::GetOpenXRHandPose(FBPOpenXRActionSkeletalData& HandPoseContainer, UOpenXRHandPoseComponent* HandPoseComponent, bool bGetMockUpPose)
{
FXRMotionControllerData MotionControllerData;
if (bGetMockUpPose)
{
GetMockUpControllerData(MotionControllerData, HandPoseContainer);
return true;
}
UHeadMountedDisplayFunctionLibrary::GetMotionControllerData((UObject*)HandPoseComponent, HandPoseContainer.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left ? EControllerHand::Left : EControllerHand::Right, MotionControllerData);
if (MotionControllerData.bValid)
{
HandPoseContainer.SkeletalTransforms.Empty(MotionControllerData.HandKeyPositions.Num());
FTransform ParentTrans = FTransform::Identity;
if (MotionControllerData.DeviceVisualType == EXRVisualType::Controller)
{
ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
}
else // EXRVisualType::Hand visual type
{
ParentTrans = FTransform(MotionControllerData.HandKeyRotations[(uint8)EHandKeypoint::Palm], MotionControllerData.HandKeyPositions[(uint8)EHandKeypoint::Palm], FVector(1.f));
}
for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); ++i)
{
// Convert to component space, we convert then to parent space later when applying it
HandPoseContainer.SkeletalTransforms.Add(FTransform(MotionControllerData.HandKeyRotations[i].GetNormalized(), MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
}
//if (bGetCurlValues)
{
GetFingerCurlValues(HandPoseContainer.SkeletalTransforms, HandPoseContainer.FingerCurls);
}
HandPoseContainer.bHasValidData = (HandPoseContainer.SkeletalTransforms.Num() == EHandKeypointCount);
return true;
}
HandPoseContainer.bHasValidData = false;
return false;
}
void UOpenXRExpansionFunctionLibrary::GetFingerCurlValues(TArray<FTransform>& TransformArray, TArray<float>& CurlArray)
{
// Fail if the count is too low
if (TransformArray.Num() < EHandKeypointCount)
return;
if (CurlArray.Num() < 5)
{
CurlArray.AddZeroed(5);
}
CurlArray[0] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::ThumbMetacarpal);
CurlArray[1] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::IndexProximal);
CurlArray[2] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::MiddleProximal);
CurlArray[3] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::RingProximal);
CurlArray[4] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::LittleProximal);
}
bool UOpenXRExpansionFunctionLibrary::GetOpenXRFingerCurlValuesForHand(
UObject* WorldContextObject,
EControllerHand TargetHand,
float& ThumbCurl,
float& IndexCurl,
float& MiddleCurl,
float& RingCurl,
float& PinkyCurl)
{
FXRMotionControllerData MotionControllerData;
UHeadMountedDisplayFunctionLibrary::GetMotionControllerData(WorldContextObject, TargetHand, MotionControllerData);
// Fail if the count is too low
if (MotionControllerData.HandKeyPositions.Num() < EHandKeypointCount)
return false;
FTransform ParentTrans = FTransform::Identity;
if (MotionControllerData.DeviceVisualType == EXRVisualType::Controller)
{
ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
}
else // EXRVisualType::Hand visual type
{
ParentTrans = FTransform(MotionControllerData.HandKeyRotations[(uint8)EHandKeypoint::Palm], MotionControllerData.HandKeyPositions[(uint8)EHandKeypoint::Palm], FVector(1.f));
}
TArray<FTransform> TransformArray;
TransformArray.AddUninitialized(MotionControllerData.HandKeyPositions.Num());
for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); ++i)
{
// Convert to component space, we convert then to parent space later when applying it
TransformArray[i] = FTransform(MotionControllerData.HandKeyRotations[i].GetNormalized(), MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans);
}
ThumbCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::ThumbMetacarpal);
IndexCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::IndexProximal);
MiddleCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::MiddleProximal);
RingCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::RingProximal);
PinkyCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::LittleProximal);
return true;
}
float UOpenXRExpansionFunctionLibrary::GetCurlValueForBoneRoot(TArray<FTransform>& TransformArray, EHandKeypoint RootBone)
{
float Angle1 = 0.0f;
float Angle2 = 0.0f;
float Angle1Curl = 0.0f;
float Angle2Curl = 0.0f;
if (RootBone == EHandKeypoint::ThumbMetacarpal)
{
FVector Prox = TransformArray[(uint8)RootBone].GetRotation().GetForwardVector();
FVector Inter = TransformArray[(uint8)RootBone + 1].GetRotation().GetForwardVector();
FVector Distal = TransformArray[(uint8)RootBone + 2].GetRotation().GetForwardVector();
Prox = FVector::VectorPlaneProject(Prox, FVector::UpVector);
Inter = FVector::VectorPlaneProject(Inter, FVector::UpVector);
Distal = FVector::VectorPlaneProject(Distal, FVector::UpVector);
Angle1 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Inter, Distal)));
Angle2 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Prox, Inter)));
Angle1Curl = (Angle1 - 10.0f) / 64.0f;
Angle2Curl = (Angle2 - 20.0f) / 42.0f;
}
else
{
FVector Prox = TransformArray[(uint8)RootBone].GetRotation().GetForwardVector();
FVector Inter = TransformArray[(uint8)RootBone + 1].GetRotation().GetForwardVector();
FVector Distal = TransformArray[(uint8)RootBone + 2].GetRotation().GetForwardVector();
// We don't use the Y (splay) value, only X and Z plane
Prox = FVector::VectorPlaneProject(Prox, FVector::RightVector);
Inter = FVector::VectorPlaneProject(Inter, FVector::RightVector);
Distal = FVector::VectorPlaneProject(Distal, FVector::RightVector);
Angle1 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Inter, Distal)));
Angle2 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Prox, Inter)));
Angle1Curl = (Angle1 - 10.0f) / 60.0f;
Angle2Curl = (Angle2 - 10.0f) / 100.0f;
}
// Can lower number of variables by doing these
float FinalAngleAvg = FMath::Clamp((Angle1Curl + Angle2Curl) / 2.0f, 0.0f, 1.0f);
//GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, FString::Printf(TEXT("Finger Curl %f"), IndexCurl));
return FinalAngleAvg;
}
void UOpenXRExpansionFunctionLibrary::ConvertHandTransformsSpaceAndBack(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms)
{
// Fail if the count is too low
if (WorldTransforms.Num() < EHandKeypointCount)
return;
if (OutTransforms.Num() < WorldTransforms.Num())
{
OutTransforms.Empty(WorldTransforms.Num());
OutTransforms.AddUninitialized(WorldTransforms.Num());
}
// Bone/Parent map
int32 BoneParents[26] =
{
// Manually build the parent hierarchy starting at the wrist which has no parent (-1)
1, // Palm -> Wrist
-1, // Wrist -> None
1, // ThumbMetacarpal -> Wrist
2, // ThumbProximal -> ThumbMetacarpal
3, // ThumbDistal -> ThumbProximal
4, // ThumbTip -> ThumbDistal
1, // IndexMetacarpal -> Wrist
6, // IndexProximal -> IndexMetacarpal
7, // IndexIntermediate -> IndexProximal
8, // IndexDistal -> IndexIntermediate
9, // IndexTip -> IndexDistal
1, // MiddleMetacarpal -> Wrist
11, // MiddleProximal -> MiddleMetacarpal
12, // MiddleIntermediate -> MiddleProximal
13, // MiddleDistal -> MiddleIntermediate
14, // MiddleTip -> MiddleDistal
1, // RingMetacarpal -> Wrist
16, // RingProximal -> RingMetacarpal
17, // RingIntermediate -> RingProximal
18, // RingDistal -> RingIntermediate
19, // RingTip -> RingDistal
1, // LittleMetacarpal -> Wrist
21, // LittleProximal -> LittleMetacarpal
22, // LittleIntermediate -> LittleProximal
23, // LittleDistal -> LittleIntermediate
24, // LittleTip -> LittleDistal
};
// Convert transforms to parent space
// The hand tracking transforms are in world space.
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
FTransform BoneTransform = WorldTransforms[Index];
BoneTransform.NormalizeRotation();
int32 ParentIndex = BoneParents[Index];
int32 ParentParent = -1;
if (ParentIndex > 0)
{
ParentParent = BoneParents[ParentIndex];
}
if (ParentIndex < 0)
{
// We are at the root, so use it.
OutTransforms[Index] = BoneTransform;
}
else
{
FTransform ParentTransform = FTransform::Identity;
// Merging missing metacarpal bone into the transform
if (ParentParent == 1) // Wrist
{
ParentTransform = WorldTransforms[ParentParent];
}
else
{
ParentTransform = WorldTransforms[ParentIndex];
}
ParentTransform.NormalizeRotation();
OutTransforms[Index] = BoneTransform.GetRelativeTransform(ParentTransform);
}
}
// Check on the easy component space conversion first
{
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
const FTransform& BoneTransform = WorldTransforms[Index];
int32 ParentIndex = BoneParents[Index];
int32 ParentParent = -1;
if (ParentIndex > 0)
{
ParentParent = BoneParents[ParentIndex];
}
if (ParentIndex > 0)
{
if (ParentParent == 1)
{
OutTransforms[Index] = OutTransforms[Index] * OutTransforms[ParentParent];
}
else
{
OutTransforms[Index] = OutTransforms[Index] * OutTransforms[ParentIndex];
}
}
}
}
}
void UOpenXRExpansionFunctionLibrary::GetMockUpControllerData(FXRMotionControllerData& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand)
{
TArray<FQuat> HandRotationsClosed = {
// Closed palm
FQuat(-6.9388939039072284e-18f,-2.7755575615628914e-17f,-5.5511151231257827e-17f,1.0000000181623150),
FQuat(0.0010158333104005046f,-0.031842494413126823f,0.0082646248419453450f,-0.99945823120983279),
FQuat(0.49284713488980170f,-0.22607130273287540f,-0.44054329019960731f,0.71548243409346490),
FQuat(-0.60572821120045273f,-0.017497510794223320f,0.10943807503774633f,-0.78791529705201735),
FQuat(-0.61281359708005512f,-0.23245447006924613f,-0.058766632856478873f,-0.75297472319193537),
FQuat(-0.61281359708005512f,-0.23245447006924613f,-0.058766632856478873f,-0.75297472319193537),
FQuat(-0.11514346379358367f,-0.029229397612833233f,-0.076351329575539195f,0.98997885626789228),
FQuat(0.079333339113826437f,-0.72590009974051883f,0.050346047334316274f,-0.68135202233808378),
FQuat(0.0032539550806410245f,-0.97123336736010268f,0.098921247251489333f,0.21658665949113542),
FQuat(-0.064585069112672477f,-0.57963374972897053f,0.075445998290538954f,0.80880246209406348),
FQuat(0.064585069112672477f,0.57963374972897053f,-0.075445998290538954f,-0.80880246209406348),
FQuat(-7.7702472130181111e-08f,9.8656527815210726e-08f,1.3838491007001075e-06f,1.0000000181613493),
FQuat(0.085300231549708214f,-0.74048187833139134f,0.058532016219761618f,-0.66406663653752407),
FQuat(0.011595964719175678f,-0.98786834549923641f,0.099110835214894707f,0.11899052159928070),
FQuat(-0.063530326287074640f,-0.56012988021281451f,0.076145543493668810f,0.82244775363868539),
FQuat(0.030477923994508188f,0.55662337489051250f,-0.098559802475379960f,-0.82433459003921772),
FQuat(-0.025910339872061101f,0.052311401725670628f,0.042953782692297438f,0.99737013210455761),
FQuat(0.11635892750396379f,-0.74717191584145570f,-0.026909776929005647f,-0.65381238012001353),
FQuat(0.098078041656806447f,-0.98532297068866348f,0.071135591008198620f,0.12024601945419003),
FQuat(0.0028091467060491482f,-0.52817558741956572f,0.11864668261714340f,0.84080060571895254),
FQuat(-0.0028091467060491482f,0.52817558741956572f,-0.11864668261714340f,-0.84080060571895254),
FQuat(-0.11913260527111892f,0.10934177100849747f,0.11664955310670821f,0.97992077106196507),
FQuat(-0.18696185363314571f,0.78123174637415782f,0.043590203318890380f,0.59398834520762267),
FQuat(0.15903913486884827f,-0.97287570092949460f,0.091728860868847406f,0.14073122087670015),
FQuat(0.035141532005084519f,-0.48251853052338572f,0.18910886397987722f,0.85450501128943579),
FQuat(0.035141532005084519f,-0.48251853052338572f,0.18910886397987722f,0.85450501128943579)
};
TArray<FQuat> HandRotationsOpen = {
// Open Hand
FQuat(0.167000905f,-0.670308471f,0.304047525f,-0.656011939f),
FQuat(-0.129862994f,0.736467659f,-0.315623045f,0.584065497f),
FQuat(-0.030090153f,0.121532254f,-0.840178490f,0.527659237f),
FQuat(-0.126470163f,-0.262596279f,0.816956878f,-0.497623593f),
FQuat(-0.102322638f,-0.249194950f,0.821705163f,-0.502227187f),
FQuat(-0.102322638f,-0.249194950f,0.821705163f,-0.502227187f),
FQuat(-0.277370781f,0.686735749f,-0.258646101f,0.620130479f),
FQuat(0.193366051f,-0.808131576f,0.260262072f,-0.491728455f),
FQuat(0.145547777f,-0.854364336f,0.317562312f,-0.384749293f),
FQuat(0.107193023f,-0.879853010f,0.321882188f,-0.332806766f),
FQuat(0.107193023f,-0.879853010f,0.321882188f,-0.332806766f),
FQuat(0.166999936f,-0.670307159f,0.304047883f,-0.656013489f),
FQuat(0.206125781f,-0.815250278f,0.203173012f,-0.501596987f),
FQuat(0.164493740f,-0.890369833f,0.257293820f,-0.337613612f),
FQuat(0.114019498f,-0.937856555f,0.283619940f,-0.164267495f),
FQuat(0.107336335f,-0.925720870f,0.321023613f,-0.168710276f),
FQuat(0.156629071f,-0.719596088f,0.210793152f,-0.642817795f),
FQuat(0.194258988f,-0.858762920f,0.127883837f,-0.456546605f),
FQuat(0.166189745f,-0.930981100f,0.161785051f,-0.281922638f),
FQuat(0.119936436f,-0.970744252f,0.189072192f,-0.086731322f),
FQuat(0.119936436f,-0.970744252f,0.189072192f,-0.086731322f),
FQuat(0.160288095f,-0.812664807f,0.100923792f,-0.551087677f),
FQuat(0.207311243f,-0.870556056f,0.056644741f,-0.442656904f),
FQuat(0.191506147f,-0.944826961f,0.096772499f,-0.247511998f),
FQuat(0.116890728f,-0.981477261f,0.138804480f,-0.061412390f),
FQuat(0.116890728f,-0.981477261f,0.138804480f,-0.061412390f)
};
MotionControllerData.HandKeyRotations = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandRotationsOpen :*/ HandRotationsClosed;
TArray<FVector> HandPositionsClosed = {
// Closed palm - Left
FVector(0.0000000000000000f,0.0000000000000000f,0.0000000000000000f),
FVector(-2.8690212431406792f,0.70708009295073815f,-0.47404338536985718f),
FVector(-1.1322360817697272f,-2.1125772981974671f,-1.2278703475596775f),
FVector(0.92697682070144727f,-5.5601677377459957f,-1.6753327187355360f),
FVector(4.0987554778339428f,-6.0520138168462640f,-2.1960898756852747f),
FVector(5.5854053809842918f,-5.4247506634349065f,-2.6631245417791525f),
FVector(-1.6026417502203387f,-1.4646945203797794f,-0.17057236434820122f),
FVector(5.7352432311721007f,-2.5389617545260998f,0.39061644722637634f),
FVector(5.4801464829170561f,-3.3344912297783416f,-3.8566611419550343f),
FVector(2.9179605371815693f,-3.2311985822561073f,-2.6652727318443148f),
FVector(3.2708935578922342f,-3.0117453368521279f,-1.6311186720587312f),
FVector(-1.1935619377191149f,-1.8034793735494103e-05f,1.4147048846974153e-06f),
FVector(5.9028526610092893f,1.3513666817788206e-05f,-4.3170989212359956e-06f),
FVector(5.4567759872551527f,-0.87968929643487392f,-4.1965100581882382f),
FVector(2.2252652348065158f,-0.87742006725177069f,-3.4067970851427791f),
FVector(2.6916877869696085f,-0.62360084690574125f,-2.2285708116727738f),
FVector(-1.1796822503165614f,1.3653411443775685f,-0.17694011615865479f),
FVector(5.3502831208188670f,1.9121382570769896f,-0.87930289382919313f),
FVector(4.8743654830862742f,1.3526757302541959f,-4.8457258101076217f),
FVector(2.1622015314362244f,0.85068796311660544f,-4.1307752690132205f),
FVector(2.6021369184528194f,1.0596020074600654f,-3.1860412064934174f),
FVector(-1.2905361603753163f,2.6108535683555365f,-0.46423293549223010f),
FVector(4.6820094577722964f,3.8858425146699327f,-1.9880098921962746f),
FVector(4.0115118130532306f,3.1678700881616777f,-4.8092930847360869f),
FVector(2.3757993445967389f,2.6579252395479291f,-4.2645319235961239f),
FVector(2.7329133227289351f,2.8811366857469527f,-3.6179750674182261f)
};
// Open Hand
TArray<FVector> HandPositionsOpen = {
FVector(-1014.001f,-478.278f,212.902f),
FVector(-1013.516f,-476.006f,214.688f),
FVector(-1016.362f,-479.642f,215.119f),
FVector(-1018.145f,-483.254f,214.805f),
FVector(-1019.682f,-485.682f,213.284f),
FVector(-1020.480f,-486.982f,212.581f),
FVector(-1014.360f,-478.927f,215.169f),
FVector(-1014.932f,-484.146f,209.902f),
FVector(-1016.872f,-486.643f,206.852f),
FVector(-1018.771f,-488.058f,205.231f),
FVector(-1019.613f,-488.507f,204.655f),
FVector(-1013.901f,-477.534f,213.831f),
FVector(-1014.494f,-481.954f,208.310f),
FVector(-1016.269f,-484.282f,205.146f),
FVector(-1018.657f,-485.834f,203.427f),
FVector(-1019.846f,-486.231f,203.113f),
FVector(-1013.816f,-476.436f,213.006f),
FVector(-1014.637f,-479.707f,207.344f),
FVector(-1016.703f,-481.540f,204.355f),
FVector(-1018.962f,-482.692f,203.000f),
FVector(-1019.978f,-482.975f,202.870f),
FVector(-1013.845f,-475.325f,212.363f),
FVector(-1015.993f,-477.665f,206.928f),
FVector(-1017.571f,-478.907f,204.670f),
FVector(-1019.033f,-479.652f,203.887f),
FVector(-1019.778f,-479.842f,203.819f)
};
MotionControllerData.HandKeyPositions = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandPositionsOpen : */HandPositionsClosed;
if (SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left)
{
MotionControllerData.GripPosition = FVector(-1018.305f, -478.019f, 209.872f);
MotionControllerData.GripRotation = FQuat(-0.116352126f, 0.039430488f, -0.757644236f, 0.641001403f);
}
else
{
MotionControllerData.GripPosition = FVector(-1202.619f, -521.077f, 283.076f);
MotionControllerData.GripRotation = FQuat(0.040843058f, 0.116659224f, 0.980030060f, -0.155767411f);
}
MotionControllerData.DeviceName = TEXT("OpenXR");
SkeletalMappingData.SkeletalTransforms.Empty(SkeletalMappingData.SkeletalTransforms.Num());
FTransform ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); i++)
{
SkeletalMappingData.SkeletalTransforms.Add(FTransform(MotionControllerData.HandKeyRotations[i], MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
}
SkeletalMappingData.bHasValidData = (SkeletalMappingData.SkeletalTransforms.Num() == EHandKeypointCount);
}

View file

@ -0,0 +1,24 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "OpenXRExpansionPlugin.h"
#include "OpenXRExpansionFunctionLibrary.h"
#define LOCTEXT_NAMESPACE "FXRExpansionPluginModule"
void FOpenXRExpansionPluginModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
//LoadOpenVRModule();
}
void FOpenXRExpansionPluginModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
// UnloadOpenVRModule();
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FOpenXRExpansionPluginModule, OpenXRExpansionPlugin)

View file

@ -0,0 +1,884 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "OpenXRHandPoseComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(OpenXRHandPoseComponent)
#include "Net/UnrealNetwork.h"
#include "MotionControllerComponent.h"
#include "OpenXRExpansionFunctionLibrary.h"
#include "Engine/NetSerialization.h"
#include "XRMotionControllerBase.h" // for GetHandEnumForSourceName()
//#include "EngineMinimal.h"
UOpenXRHandPoseComponent::UOpenXRHandPoseComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
ReplicationRateForSkeletalAnimations = 10.f;
bReplicateSkeletalData = false;
bSmoothReplicatedSkeletalData = true;
SkeletalNetUpdateCount = 0.f;
bDetectGestures = true;
SetIsReplicatedByDefault(true);
bGetMockUpPoseForDebugging = false;
}
void UOpenXRHandPoseComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// Skipping the owner with this as the owner will use the controllers location directly
DOREPLIFETIME_CONDITION(UOpenXRHandPoseComponent, LeftHandRep, COND_SkipOwner);
DOREPLIFETIME_CONDITION(UOpenXRHandPoseComponent, RightHandRep, COND_SkipOwner);
}
void UOpenXRHandPoseComponent::Server_SendSkeletalTransforms_Implementation(const FBPXRSkeletalRepContainer& SkeletalInfo)
{
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandSkeletalActions[i].TargetHand == SkeletalInfo.TargetHand)
{
if (SkeletalInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(SkeletalInfo, HandSkeletalActions[i]);
LeftHandRep = SkeletalInfo;
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
}
else
{
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(SkeletalInfo, HandSkeletalActions[i]);
RightHandRep = SkeletalInfo;
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
}
break;
}
}
}
bool UOpenXRHandPoseComponent::Server_SendSkeletalTransforms_Validate(const FBPXRSkeletalRepContainer& SkeletalInfo)
{
return true;
}
void FOpenXRAnimInstanceProxy::PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds)
{
Super::PreUpdate(InAnimInstance, DeltaSeconds);
if (UOpenXRAnimInstance* OwningInstance = Cast<UOpenXRAnimInstance>(InAnimInstance))
{
if (OwningInstance->OwningPoseComp)
{
if (HandSkeletalActionData.Num() != OwningInstance->OwningPoseComp->HandSkeletalActions.Num())
{
HandSkeletalActionData.Empty(OwningInstance->OwningPoseComp->HandSkeletalActions.Num());
for(FBPOpenXRActionSkeletalData& actionInfo : OwningInstance->OwningPoseComp->HandSkeletalActions)
{
HandSkeletalActionData.Add(actionInfo);
}
}
else
{
for (int i = 0; i < OwningInstance->OwningPoseComp->HandSkeletalActions.Num(); ++i)
{
HandSkeletalActionData[i] = OwningInstance->OwningPoseComp->HandSkeletalActions[i];
}
}
}
}
}
FOpenXRAnimInstanceProxy::FOpenXRAnimInstanceProxy(UAnimInstance* InAnimInstance)
: FAnimInstanceProxy(InAnimInstance)
{
}
void UOpenXRHandPoseComponent::BeginPlay()
{
/*if (UMotionControllerComponent * MotionParent = Cast<UMotionControllerComponent>(GetAttachParent()))
{
EControllerHand HandType;
if (!FXRMotionControllerBase::GetHandEnumForSourceName(MotionParent->MotionSource, HandType))
{
HandType = EControllerHand::Left;
}
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandType == EControllerHand::Left || HandType == EControllerHand::AnyHand)
HandSkeletalActions[i].SkeletalData.TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Left;
else
HandSkeletalActions[i].SkeletalData.TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
}
}*/
Super::BeginPlay();
}
void UOpenXRHandPoseComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
if (!IsLocallyControlled())
{
if (bReplicateSkeletalData)
{
// Handle bone lerping here if we are replicating
for (FBPOpenXRActionSkeletalData& actionInfo : HandSkeletalActions)
{
if (bSmoothReplicatedSkeletalData)
{
if (actionInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
LeftHandRepManager.UpdateManager(DeltaTime, actionInfo, this);
}
else
{
RightHandRepManager.UpdateManager(DeltaTime, actionInfo, this);
}
}
}
}
}
else // Get data and process
{
bool bGetCompressedTransforms = false;
if (bReplicateSkeletalData && HandSkeletalActions.Num() > 0)
{
SkeletalNetUpdateCount += DeltaTime;
if (SkeletalNetUpdateCount >= (1.0f / ReplicationRateForSkeletalAnimations))
{
SkeletalNetUpdateCount = 0.0f;
bGetCompressedTransforms = true;
}
}
for (FBPOpenXRActionSkeletalData& actionInfo : HandSkeletalActions)
{
if (UOpenXRExpansionFunctionLibrary::GetOpenXRHandPose(actionInfo, this, bGetMockUpPoseForDebugging))
{
if (bGetCompressedTransforms)
{
if (GetNetMode() == NM_Client)
{
if (actionInfo.bHasValidData)
{
FBPXRSkeletalRepContainer ContainerSend;
ContainerSend.CopyForReplication(actionInfo);
Server_SendSkeletalTransforms(ContainerSend);
}
}
else
{
if (actionInfo.bHasValidData)
{
if (actionInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
LeftHandRep.CopyForReplication(actionInfo);
else
RightHandRep.CopyForReplication(actionInfo);
}
}
}
}
if (bDetectGestures && actionInfo.bHasValidData && actionInfo.SkeletalTransforms.Num() > 0 && GesturesDB != nullptr && GesturesDB->Gestures.Num() > 0)
{
DetectCurrentPose(actionInfo);
}
}
}
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
bool UOpenXRHandPoseComponent::SaveCurrentPose(FName RecordingName, EVRSkeletalHandIndex HandToSave)
{
if (!HandSkeletalActions.Num())
return false;
// Default to the first hand element so that single length arrays work as is.
FBPOpenXRActionSkeletalData* HandSkeletalAction = nullptr;
// Now check for the specific passed in hand if this is a multi hand
for (int i = 0; i < HandSkeletalActions.Num(); ++i)
{
if (HandSkeletalActions[i].TargetHand == HandToSave)
{
HandSkeletalAction = &HandSkeletalActions[i];
break;
}
}
if (!HandSkeletalAction || !HandSkeletalAction->bHasValidData || HandSkeletalAction->SkeletalTransforms.Num() < EHandKeypointCount)
return false;
if (GesturesDB)
{
FOpenXRGesture NewGesture;
int32 FingerMap[5] =
{
(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
};
FVector WristLoc = FVector::ZeroVector;
if (HandToSave == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
WristLoc = HandSkeletalAction->SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
}
else
{
WristLoc = HandSkeletalAction->SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
}
for (int i = 0; i < 5; ++i)
{
if (HandToSave == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
NewGesture.FingerValues[i] = FOpenXRGestureFingerPosition(HandSkeletalAction->SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc, (EXRHandJointType)FingerMap[i]);
}
else
{
NewGesture.FingerValues[i] = FOpenXRGestureFingerPosition(HandSkeletalAction->SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc, (EXRHandJointType)FingerMap[i]);
}
}
NewGesture.Name = RecordingName;
GesturesDB->Gestures.Add(NewGesture);
return true;
}
return false;
}
bool UOpenXRHandPoseComponent::K2_DetectCurrentPose(UPARAM(ref) FBPOpenXRActionSkeletalData& SkeletalAction, FOpenXRGesture & GestureOut)
{
if (!GesturesDB || GesturesDB->Gestures.Num() < 1)
return false;
int32 FingerMap[5] =
{
(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
};
FVector WristLoc = FVector::ZeroVector;
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
}
else
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
}
// Early fill in an array to keep from performing math for each gesture
TArray<FVector> CurrentTips;
CurrentTips.AddUninitialized(5);
for (int i = 0; i < 5; ++i)
{
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc;
}
else
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc;
}
}
for (const FOpenXRGesture& Gesture : GesturesDB->Gestures)
{
// If not enough indexs to match curl values, or if this gesture requires finger splay and the controller can't do it
if (Gesture.FingerValues.Num() < 5 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
continue;
bool bDetectedPose = true;
for (int i = 0; i < 5; ++i)
{
FVector GestureV = Gesture.FingerValues[i].Value;
FVector CurrentV = CurrentTips[i];
FVector Difference = GestureV - CurrentV;
if (!Gesture.FingerValues[i].Value.Equals(CurrentTips[i], Gesture.FingerValues[i].Threshold))
{
bDetectedPose = false;
break;
}
}
if (bDetectedPose)
{
GestureOut = Gesture;
return true;
}
}
return false;
}
bool UOpenXRHandPoseComponent::DetectCurrentPose(FBPOpenXRActionSkeletalData &SkeletalAction)
{
if (!GesturesDB || GesturesDB->Gestures.Num() < 1 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
return false;
FTransform BoneTransform = FTransform::Identity;
int32 FingerMap[5] =
{
(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
};
FVector WristLoc = FVector::ZeroVector;
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
}
else
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
}
// Early fill in an array to keep from performing math for each gesture
TArray<FVector> CurrentTips;
CurrentTips.AddUninitialized(5);
for (int i = 0; i < 5; ++i)
{
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc;
}
else
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc;
}
}
for (auto GestureIterator = GesturesDB->Gestures.CreateConstIterator(); GestureIterator; ++GestureIterator)
{
const FOpenXRGesture &Gesture = *GestureIterator;
// If not enough indexs to match curl values, or if this gesture requires finger splay and the controller can't do it
if (Gesture.FingerValues.Num() < 5 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
continue;
bool bDetectedPose = true;
for (int i = 0; i < 5; ++i)
{
if (Gesture.FingerValues[i].Threshold <= 0.0f)
continue;
if (!Gesture.FingerValues[i].Value.Equals(CurrentTips[i], Gesture.FingerValues[i].Threshold))
{
bDetectedPose = false;
break;
}
}
if (bDetectedPose)
{
if (SkeletalAction.LastHandGesture != Gesture.Name)
{
if (SkeletalAction.LastHandGesture != NAME_None)
OnGestureEnded.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
SkeletalAction.LastHandGesture = Gesture.Name;
SkeletalAction.LastHandGestureIndex = GestureIterator.GetIndex();
OnNewGestureDetected.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
return true;
}
else
return false; // Same gesture
}
}
if (SkeletalAction.LastHandGesture != NAME_None)
{
OnGestureEnded.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
SkeletalAction.LastHandGesture = NAME_None;
SkeletalAction.LastHandGestureIndex = INDEX_NONE;
}
return false;
}
UOpenXRHandPoseComponent::FTransformLerpManager::FTransformLerpManager()
{
bReplicatedOnce = false;
bLerping = false;
UpdateCount = 0.0f;
UpdateRate = 0.0f;
}
void UOpenXRHandPoseComponent::FTransformLerpManager::PreCopyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing)
{
if (!bExponentialSmoothing)
{
if (ActionInfo.SkeletalTransforms.Num())
{
OldTransforms = ActionInfo.SkeletalTransforms;
}
}
}
void UOpenXRHandPoseComponent::FTransformLerpManager::NotifyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing)
{
UpdateRate = (1.0f / NetUpdateRate);
if (bReplicatedOnce)
{
bLerping = true;
UpdateCount = 0.0f;
NewTransforms = ActionInfo.SkeletalTransforms;
ActionInfo.SkeletalTransforms = OldTransforms;
}
else
{
if (bExponentialSmoothing)
{
OldTransforms = ActionInfo.SkeletalTransforms;
}
bReplicatedOnce = true;
}
}
void UOpenXRHandPoseComponent::FTransformLerpManager::UpdateManager(float DeltaTime, FBPOpenXRActionSkeletalData& ActionInfo, UOpenXRHandPoseComponent* ParentComp)
{
if (!ActionInfo.bHasValidData || !OldTransforms.Num())
return;
if (bLerping)
{
bool bExponentialSmoothing = ParentComp->bUseExponentialSmoothing;
float LerpVal = 0.0f;
if (!bExponentialSmoothing || ParentComp->InterpolationSpeed <= 0.f)
{
UpdateCount += DeltaTime;
// Keep LerpVal
LerpVal = FMath::Clamp(UpdateCount / UpdateRate, 0.0f, 1.0f);
}
else
{
LerpVal = FMath::Clamp(DeltaTime * ParentComp->InterpolationSpeed, 0.f, 1.f);
}
if (!bExponentialSmoothing && LerpVal >= 1.0f)
{
bLerping = false;
UpdateCount = 0.0f;
ActionInfo.SkeletalTransforms = NewTransforms;
}
else
{
int32 BoneCountAdjustment = 6 + (ActionInfo.bEnableUE4HandRepSavings ? 4 : 0);
if ((NewTransforms.Num() < (EHandKeypointCount - BoneCountAdjustment)) || (NewTransforms.Num() != ActionInfo.SkeletalTransforms.Num()))
{
return;
}
ActionInfo.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_PALM_EXT] = FTransform::Identity;
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_Thumb3, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
// These are copied from the 3rd joints as they use the same transform but a different root
// Don't want to waste cpu time blending these
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_Thumb] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Thumb2];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_IndexFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_IndexFinger3];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_MiddleFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_MiddleFinger3];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_RingFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_RingFinger3];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_PinkyFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_PinkyFinger3];
}
}
}
void FBPXRSkeletalRepContainer::CopyForReplication(FBPOpenXRActionSkeletalData& Other)
{
TargetHand = Other.TargetHand;
if (!Other.bHasValidData)
return;
bAllowDeformingMesh = Other.bAllowDeformingMesh;
bEnableUE4HandRepSavings = Other.bEnableUE4HandRepSavings;
// Instead of doing this, we likely need to lerp but this is for testing
//SkeletalTransforms = Other.SkeletalData.SkeletalTransforms;
if (Other.SkeletalTransforms.Num() < EHandKeypointCount)
{
SkeletalTransforms.Empty();
return;
}
int32 BoneCountAdjustment = 6 + (bEnableUE4HandRepSavings ? 4 : 0);
if (SkeletalTransforms.Num() != EHandKeypointCount - BoneCountAdjustment)
{
SkeletalTransforms.Reset(EHandKeypointCount - BoneCountAdjustment); // Minus bones we don't need
SkeletalTransforms.AddUninitialized(EHandKeypointCount - BoneCountAdjustment);
}
int32 idx = 0;
// Root is always identity
//SkeletalTransforms[0] = Other.SkeletalData.SkeletalTransforms[(uint8)EVROpenInputBones::eBone_Root]; // This has no pos right? Need to skip pos on it
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT];
}
void FBPXRSkeletalRepContainer::CopyReplicatedTo(const FBPXRSkeletalRepContainer& Container, FBPOpenXRActionSkeletalData& Other)
{
int32 BoneCountAdjustment = 6 + (Container.bEnableUE4HandRepSavings ? 4 : 0);
if (Container.SkeletalTransforms.Num() < (EHandKeypointCount - BoneCountAdjustment))
{
Other.SkeletalTransforms.Empty();
Other.bHasValidData = false;
return;
}
Other.bAllowDeformingMesh = Container.bAllowDeformingMesh;
Other.bEnableUE4HandRepSavings = Container.bEnableUE4HandRepSavings;
// Instead of doing this, we likely need to lerp but this is for testing
//Other.SkeletalData.SkeletalTransforms = Container.SkeletalTransforms;
if (Other.SkeletalTransforms.Num() != EHandKeypointCount)
{
Other.SkeletalTransforms.Reset(EHandKeypointCount);
Other.SkeletalTransforms.AddUninitialized(EHandKeypointCount);
}
int32 idx = 0;
// Only fill in the ones that we care about
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_PALM_EXT] = FTransform::Identity; // Always identity
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT] = FTransform::Identity;
Other.bHasValidData = true;
}
bool FBPXRSkeletalRepContainer::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
bOutSuccess = true;
Ar.SerializeBits(&TargetHand, 1);
Ar.SerializeBits(&bAllowDeformingMesh, 1);
Ar.SerializeBits(&bEnableUE4HandRepSavings, 1);
int32 BoneCountAdjustment = 6 + (bEnableUE4HandRepSavings ? 4 : 0);
uint8 TransformCount = EHandKeypointCount - BoneCountAdjustment;
bool bHasValidData = SkeletalTransforms.Num() >= TransformCount;
Ar.SerializeBits(&bHasValidData, 1);
//Ar << TransformCount;
if (Ar.IsLoading())
{
SkeletalTransforms.Reset(TransformCount);
}
FVector Position = FVector::ZeroVector;
FRotator Rot = FRotator::ZeroRotator;
if (bHasValidData)
{
for (int i = 0; i < TransformCount; i++)
{
if (Ar.IsSaving())
{
if (bAllowDeformingMesh)
Position = SkeletalTransforms[i].GetLocation();
Rot = SkeletalTransforms[i].Rotator();
}
if (bAllowDeformingMesh)
bOutSuccess &= SerializePackedVector<10, 11>(Position, Ar);
Rot.SerializeCompressed(Ar); // Short? 10 bit?
if (Ar.IsLoading())
{
if (bAllowDeformingMesh)
SkeletalTransforms.Add(FTransform(Rot, Position));
else
SkeletalTransforms.Add(FTransform(Rot));
}
}
}
return bOutSuccess;
}
void UOpenXRAnimInstance::NativeBeginPlay()
{
Super::NativeBeginPlay();
AActor* Owner = GetOwningComponent()->GetOwner();
UActorComponent* HandPoseComp = nullptr;
if (Owner)
{
HandPoseComp = Owner->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
if (!HandPoseComp)
{
// We are also checking owner->owner in case hand mesh is in a sub actor
if (Owner->GetOwner())
{
HandPoseComp = Owner->GetOwner()->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
}
}
}
if (!HandPoseComp)
{
return;
}
if (UOpenXRHandPoseComponent* HandComp = Cast<UOpenXRHandPoseComponent>(HandPoseComp))
{
OwningPoseComp = HandComp;
}
}
/*void UOpenXRAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
AActor* Owner = GetOwningComponent()->GetOwner();
UActorComponent* HandPoseComp = nullptr;
if (Owner)
{
HandPoseComp = Owner->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
if (!HandPoseComp)
{
// We are also checking owner->owner in case hand mesh is in a sub actor
if (Owner->GetOwner())
{
HandPoseComp = Owner->GetOwner()->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
}
}
}
if (!HandPoseComp)
{
return;
}
if (UOpenXRHandPoseComponent* HandComp = Cast<UOpenXRHandPoseComponent>(HandPoseComp))
{
OwningPoseComp = HandComp;
}
}*/
void UOpenXRAnimInstance::InitializeCustomBoneMapping(UPARAM(ref) FBPOpenXRSkeletalMappingData& SkeletalMappingData)
{
USkeleton* AssetSkeleton = this->CurrentSkeleton;//RequiredBones.GetSkeletonAsset();
if (AssetSkeleton)
{
FBoneContainer& RequiredBones = this->GetRequiredBones();
for (FBPOpenXRSkeletalPair& BonePair : SkeletalMappingData.BonePairs)
{
// Fill in the bone name for the reference
BonePair.ReferenceToConstruct.BoneName = BonePair.BoneToTarget;
// Init the reference
BonePair.ReferenceToConstruct.Initialize(AssetSkeleton);
BonePair.ReferenceToConstruct.CachedCompactPoseIndex = BonePair.ReferenceToConstruct.GetCompactPoseIndex(RequiredBones);
if ((BonePair.ReferenceToConstruct.CachedCompactPoseIndex != INDEX_NONE))
{
// Get our parent bones index
BonePair.ParentReference = RequiredBones.GetParentBoneIndex(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
}
}
if (UObject* OwningAsset = RequiredBones.GetAsset())
{
SkeletalMappingData.LastInitializedName = OwningAsset->GetFName();
}
SkeletalMappingData.bInitialized = true;
return;
}
SkeletalMappingData.bInitialized = false;
}

View file

@ -0,0 +1,120 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Runtime/AnimGraphRuntime/Public/BoneControllers/AnimNode_SkeletalControlBase.h"
#include "OpenXRExpansionTypes.h"
//#include "Skeleton/BodyStateSkeleton.h"
//#include "BodyStateAnimInstance.h"
#include "AnimNode_ApplyOpenXRHandPose.generated.h"
USTRUCT()
struct OPENXREXPANSIONPLUGIN_API FAnimNode_ApplyOpenXRHandPose : public FAnimNode_SkeletalControlBase
{
GENERATED_USTRUCT_BODY()
public:
FVector WristForwardLS_UE;
FVector WristSideDirectionLS;
// Generally used when not passing in custom bone mappings, defines the auto mapping style
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
EVROpenXRSkeletonType SkeletonType;
// If your hand is part of a full body or arm skeleton and you don't have a proxy bone to retain the position enable this
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
bool bSkipRootBone;
// If you only want to use the wrist transform part of this
// This will also automatically add the deform to the wrist as it doesn't make much sense without it
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
bool bOnlyApplyWristTransform;
// Generally used when not passing in custom bone mappings, defines the auto mapping style
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
FBPOpenXRActionSkeletalData OptionalStoredActionInfo;
// MappedBonePairs, if you leave it blank then they will auto generate based off of the SkeletonType
// Otherwise, fill out yourself.
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinHiddenByDefault))
FBPOpenXRSkeletalMappingData MappedBonePairs;
bool bIsOpenInputAnimationInstance;
void ConvertHandTransformsSpace(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms, FTransform AddTrans, bool bMirrorLeftRight, bool bMergeMissingUE4Bones);
void CalculateSkeletalAdjustment(USkeleton* AssetSkeleton);
void CalculateOpenXRAdjustment();
FQuat CalcRotationAboutAxis(const FVector& FromDirection, const FVector& ToDirection, const FVector& Axis)
{
FVector FromDirectionCp = FVector::CrossProduct(Axis, FromDirection);
FVector ToDirectionCp = FVector::CrossProduct(Axis, ToDirection);
return FQuat::FindBetweenVectors(FromDirectionCp, ToDirectionCp);
}
FTransform GetRefBoneInCS(TArray<FTransform>& RefBones, TArray<FMeshBoneInfo>& RefBonesInfo, int32 BoneIndex)
{
FTransform BoneTransform;
if (BoneIndex >= 0)
{
BoneTransform = RefBones[BoneIndex];
if (RefBonesInfo[BoneIndex].ParentIndex >= 0)
{
BoneTransform *= GetRefBoneInCS(RefBones, RefBonesInfo, RefBonesInfo[BoneIndex].ParentIndex);
}
}
return BoneTransform;
}
void SetVectorToMaxElement(FVector& vec)
{
FVector absVal = vec.GetAbs();
if (absVal.X > absVal.Y && absVal.X > absVal.Z)
{
vec = vec.GetSignVector() * FVector(1.0f, 0.f, 0.f);
vec.Normalize();
}
else if (absVal.Y > absVal.X && absVal.Y > absVal.Z)
{
vec = vec.GetSignVector() * FVector(0.0f, 1.f, 0.f);
vec.Normalize();
}
else if (absVal.Z > absVal.X && absVal.Z > absVal.Y)
{
vec = vec.GetSignVector() * FVector(0.0f, 0.f, 1.f);
vec.Normalize();
}
else
{
vec.Normalize();
}
}
// FAnimNode_SkeletalControlBase interface
//virtual void UpdateInternal(const FAnimationUpdateContext& Context) override;
virtual void EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms) override;
virtual bool IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones) override;
// End of FAnimNode_SkeletalControlBase interface
virtual void OnInitializeAnimInstance(const FAnimInstanceProxy* InProxy, const UAnimInstance* InAnimInstance) override;
virtual bool NeedsOnInitializeAnimInstance() const override { return true; }
virtual void InitializeBoneReferences(const FBoneContainer& RequiredBones) override;
virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) override;
// Constructor
FAnimNode_ApplyOpenXRHandPose();
protected:
bool WorldIsGame;
AActor* OwningActor;
private:
};

View file

@ -0,0 +1,102 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "UObject/Object.h"
#include "Engine/EngineTypes.h"
#include "HeadMountedDisplayTypes.h"
#if defined(OPENXR_SUPPORTED)
#include "OpenXRCore.h"
#include "OpenXRHMD.h"
#endif
#include "OpenXRExpansionTypes.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "OpenXRExpansionFunctionLibrary.generated.h"
#if !defined(OPENXR_SUPPORTED)
class FOpenXRHMD;
#endif
DECLARE_LOG_CATEGORY_EXTERN(OpenXRExpansionFunctionLibraryLog, Log, All);
// This needs to be updated as the original gets changed, that or hope they make the original blueprint accessible.
UENUM(Blueprintable)
enum class EBPOpenXRControllerDeviceType : uint8
{
DT_SimpleController,
DT_ValveIndexController,
DT_ViveController,
DT_ViveProController,
//DT_CosmosController,
DT_DaydreamController,
DT_OculusTouchController,
DT_OculusGoController,
DT_MicrosoftMotionController,
DT_MicrosoftXboxController,
DT_PicoNeo3Controller,
DT_WMRController,
DT_UnknownController
};
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent))
class OPENXREXPANSIONPLUGIN_API UOpenXRExpansionFunctionLibrary : public UBlueprintFunctionLibrary
{
//GENERATED_BODY()
GENERATED_BODY()
public:
UOpenXRExpansionFunctionLibrary(const FObjectInitializer& ObjectInitializer);
~UOpenXRExpansionFunctionLibrary();
public:
static FOpenXRHMD* GetOpenXRHMD()
{
#if defined(OPENXR_SUPPORTED)
static FName SystemName(TEXT("OpenXR"));
if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
{
return static_cast<FOpenXRHMD*>(GEngine->XRSystem.Get());
}
#endif
return nullptr;
}
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static bool GetOpenXRHandPose(FBPOpenXRActionSkeletalData& HandPoseContainer, UOpenXRHandPoseComponent* HandPoseComponent, bool bGetMockUpPose = false);
//UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static void GetFingerCurlValues(TArray<FTransform>& TransformArray, TArray<float>& CurlArray);
// Get the estimated curl values from hand tracking
// Will return true if it was able to get the curls, false if it could not (hand tracking not enabled or no data for the tracked index)
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (WorldContext = "WorldContextObject"))
static bool GetOpenXRFingerCurlValuesForHand(
UObject* WorldContextObject,
EControllerHand TargetHand,
float& ThumbCurl,
float& IndexCurl,
float& MiddleCurl,
float& RingCurl,
float& PinkyCurl);
static float GetCurlValueForBoneRoot(TArray<FTransform>& TransformArray, EHandKeypoint RootBone);
//UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static void ConvertHandTransformsSpaceAndBack(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms);
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static void GetMockUpControllerData(FXRMotionControllerData& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand = false);
// Get a list of all currently tracked devices and their types, index in the array is their device index
// Returns failed if the openXR query failed (no interaction profile yet or openXR is not running)
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true", ExpandEnumAsExecs = "Result"))
static void GetXRMotionControllerType(FString& TrackingSystemName, EBPOpenXRControllerDeviceType& DeviceType, EBPXRResultSwitch &Result);
};

View file

@ -0,0 +1,18 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Modules/ModuleManager.h"
class FOpenXRExpansionPluginModule : public IModuleInterface
{
public:
FOpenXRExpansionPluginModule()
{
}
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View file

@ -0,0 +1,479 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "UObject/Object.h"
#include "Engine/EngineTypes.h"
#include "OpenXRExpansionTypes.generated.h"
// This makes a lot of the blueprint functions cleaner
UENUM()
enum class EBPXRResultSwitch : uint8
{
// On Success
OnSucceeded,
// On Failure
OnFailed
};
UENUM(BlueprintType)
enum class EVRSkeletalHandIndex : uint8
{
EActionHandIndex_Left = 0,
EActionHandIndex_Right
};
UENUM(BlueprintType)
enum class EXRHandJointType : uint8
{
OXR_HAND_JOINT_PALM_EXT = 0,
OXR_HAND_JOINT_WRIST_EXT = 1,
OXR_HAND_JOINT_THUMB_METACARPAL_EXT = 2,
OXR_HAND_JOINT_THUMB_PROXIMAL_EXT = 3,
OXR_HAND_JOINT_THUMB_DISTAL_EXT = 4,
OXR_HAND_JOINT_THUMB_TIP_EXT = 5,
OXR_HAND_JOINT_INDEX_METACARPAL_EXT = 6,
OXR_HAND_JOINT_INDEX_PROXIMAL_EXT = 7,
OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT = 8,
OXR_HAND_JOINT_INDEX_DISTAL_EXT = 9,
OXR_HAND_JOINT_INDEX_TIP_EXT = 10,
OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT = 11,
OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT = 12,
OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT = 13,
OXR_HAND_JOINT_MIDDLE_DISTAL_EXT = 14,
OXR_HAND_JOINT_MIDDLE_TIP_EXT = 15,
OXR_HAND_JOINT_RING_METACARPAL_EXT = 16,
OXR_HAND_JOINT_RING_PROXIMAL_EXT = 17,
OXR_HAND_JOINT_RING_INTERMEDIATE_EXT = 18,
OXR_HAND_JOINT_RING_DISTAL_EXT = 19,
OXR_HAND_JOINT_RING_TIP_EXT = 20,
OXR_HAND_JOINT_LITTLE_METACARPAL_EXT = 21,
OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT = 22,
OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT = 23,
OXR_HAND_JOINT_LITTLE_DISTAL_EXT = 24,
OXR_HAND_JOINT_LITTLE_TIP_EXT = 25,
OXR_HAND_JOINT_MAX_ENUM_EXT = 0xFF
};
UENUM(BlueprintType)
enum class EVROpenXRSkeletonType : uint8
{
// UE4 Skeletal Right hand
OXR_SkeletonType_UE4Default_Right,
// UE4 Skeletal Left hand
OXR_SkeletonType_UE4Default_Left,
// OpenVR Skeletal Right hand
OXR_SkeletonType_OpenVRDefault_Right,
// OpenVR Skeletal Left hand
OXR_SkeletonType_OpenVRDefault_Left,
// UE5 Skeletal Right hand
OXR_SkeletonType_UE5Default_Right,
// UE5 Skeletal Left hand
OXR_SkeletonType_UE5Default_Left,
// OpenXR Skeletal Right hand
OXR_SkeletonType_OpenXRDefault_Right,
// OpenXR Skeletal Left hand
OXR_SkeletonType_OpenXRDefault_Left,
OXR_SkeletonType_Custom
};
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|OpenXR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPOpenXRActionSkeletalData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
EVRSkeletalHandIndex TargetHand;
// A world scale override that will replace the engines current value and force into the tracked data if non zero
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
float WorldScaleOverride;
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
bool bAllowDeformingMesh;
// If true then the bones will be mirrored from left/right, to allow you to swap a hand mesh or apply to a full body mesh
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
bool bMirrorLeftRight;
// List of aproximated curls for each finger
UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
TArray<float> FingerCurls;
UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
TArray<FTransform> SkeletalTransforms;
// If true we will assume that the target skeleton does not have the metacarpal bones and we will not replicate them
// Only really used for the old UE4 skeleton
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
bool bEnableUE4HandRepSavings;
//UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
//TArray<FTransform> OldSkeletalTransforms;
// The rotation required to rotate the finger bones back to X+
// The animation node attempts to auto calculate it, if you have a non standard hand you may need to fill
// This in by yourself
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
FTransform AdditionTransform;
UPROPERTY(NotReplicated, BlueprintReadOnly, Category = Default)
bool bHasValidData;
FName LastHandGesture;
int32 LastHandGestureIndex;
FBPOpenXRActionSkeletalData()
{
//bGetTransformsInParentSpace = false;
AdditionTransform = FTransform::Identity;// FTransform(FRotator(180.f, 0.f, -90.f), FVector::ZeroVector, FVector(1.f));//FTransform(FRotator(0.f, 90.f, 90.f), FVector::ZeroVector, FVector(1.f));
WorldScaleOverride = 0.0f;
bAllowDeformingMesh = true;
bMirrorLeftRight = false;
bEnableUE4HandRepSavings = false;
TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
bHasValidData = false;
LastHandGestureIndex = INDEX_NONE;
LastHandGesture = NAME_None;
}
};
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|SteamVR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPOpenXRSkeletalPair
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
EXRHandJointType OpenXRBone;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
FName BoneToTarget;
FBoneReference ReferenceToConstruct;
FCompactPoseBoneIndex ParentReference;
FQuat RetargetRot;
FBPOpenXRSkeletalPair() :
ParentReference(INDEX_NONE)
{
OpenXRBone = EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT;
BoneToTarget = NAME_None;
RetargetRot = FQuat::Identity;
}
FBPOpenXRSkeletalPair(EXRHandJointType Bone, FString TargetBone) :
ParentReference(INDEX_NONE)
{
OpenXRBone = Bone;
BoneToTarget = FName(*TargetBone);
ReferenceToConstruct.BoneName = BoneToTarget;
RetargetRot = FQuat::Identity;
}
FORCEINLINE bool operator==(const int32& Other) const
{
return ReferenceToConstruct.CachedCompactPoseIndex.GetInt() == Other;
//return ReferenceToConstruct.BoneIndex == Other;
}
};
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|SteamVR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPOpenXRSkeletalMappingData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
TArray<FBPOpenXRSkeletalPair> BonePairs;
TArray<int32> ReverseBonePairMap;
// Merge the transforms of bones that are missing from the OpenVR skeleton to the UE4 one.
// This should be always enabled for UE4 skeletons generally.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
bool bMergeMissingBonesUE4;
// The hand data to get, if not using a custom bone mapping then this value will be auto filled
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
EVRSkeletalHandIndex TargetHand;
FQuat AdjustmentQuat;
bool bInitialized;
FName LastInitializedName;
EVROpenXRSkeletonType LastInitializedSkeleton;
void ClearMapping()
{
bInitialized = false;
LastInitializedName = NAME_None;
AdjustmentQuat = FQuat::Identity;
LastInitializedSkeleton = EVROpenXRSkeletonType::OXR_SkeletonType_Custom;
BonePairs.Empty();
ReverseBonePairMap.Empty();
}
void ConstructReverseMapping()
{
int32 MaxElements = ((uint8)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT) + 1;
ReverseBonePairMap.Empty(MaxElements);
ReverseBonePairMap.AddUninitialized(MaxElements);
FMemory::Memset(ReverseBonePairMap.GetData(), 0, MaxElements * sizeof(int32));
for (int i = 0; i < BonePairs.Num(); ++i)
{
// Just in case someone messed up the mapping file
if (i < MaxElements)
{
ReverseBonePairMap[(uint8)BonePairs[i].OpenXRBone] = i;
}
}
}
void ConstructDefaultMappings(EVROpenXRSkeletonType SkeletonType, bool bSkipRootBone)
{
switch (SkeletonType)
{
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Right:
{
bMergeMissingBonesUE4 = false;
SetDefaultOpenVRInputs(SkeletonType, bSkipRootBone);
}break;
case EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Right:
{
bMergeMissingBonesUE4 = true;
SetDefaultUE4Inputs(SkeletonType, bSkipRootBone);
}break;
case EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Right:
{
bMergeMissingBonesUE4 = false;
SetDefaultUE5Inputs(SkeletonType, bSkipRootBone);
}break;
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Right:
{
bMergeMissingBonesUE4 = false;
SetDefaultOpenXRInputs(SkeletonType, bSkipRootBone);
}break;
}
}
void SetDefaultOpenVRInputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default OpenVR bones mapping
//if (!bSkipRootBone)
//{
//BonePairs.Add(FBPOpenVRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_PALM_EXT, FString::Printf(TEXT("Root"), HandDelimiter)));
//}
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("wrist_%s"), HandDelimiter)));
}
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("finger_thumb_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("finger_thumb_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("finger_thumb_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT, FString::Printf(TEXT("finger_thumb_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT, FString::Printf(TEXT("finger_index_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("finger_index_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_index_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("finger_index_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT, FString::Printf(TEXT("finger_index_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT, FString::Printf(TEXT("finger_middle_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("finger_middle_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_middle_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("finger_middle_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT, FString::Printf(TEXT("finger_middle_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT, FString::Printf(TEXT("finger_ring_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("finger_ring_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_ring_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("finger_ring_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT, FString::Printf(TEXT("finger_ring_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT, FString::Printf(TEXT("finger_pinky_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("finger_pinky_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_pinky_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("finger_pinky_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT, FString::Printf(TEXT("finger_pinky_%s_end"), HandDelimiter)));
// Aux bones share the final knuckles location / rotation
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("finger_thumb_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("finger_index_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("finger_middle_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("finger_ring_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("finger_pinky_%s_aux"), HandDelimiter)));
}
void SetDefaultUE4Inputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default ue4 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
}
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
}
void SetDefaultUE5Inputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default ue5 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
}
// There are inner and outer wrist elements to this, going to be anoying to map that to a single wrist index....
//OXR_HAND_JOINT_WRIST_EXT = 1,
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
}
void SetDefaultOpenXRInputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default ue5 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
}
// There are inner and outer wrist elements to this, going to be anoyying to map that to a single wrist index....
//OXR_HAND_JOINT_WRIST_EXT = 1,
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
}
FBPOpenXRSkeletalMappingData()
{
AdjustmentQuat = FQuat::Identity;
bInitialized = false;
bMergeMissingBonesUE4 = false;
TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
LastInitializedName = NAME_None;
LastInitializedSkeleton = EVROpenXRSkeletonType::OXR_SkeletonType_Custom;
}
};

View file

@ -0,0 +1,377 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "Engine/Texture.h"
#include "Engine/EngineTypes.h"
#include "HeadMountedDisplayTypes.h"
//#include "Runtime/Launch/Resources/Version.h"
#include "Animation/AnimInstanceProxy.h"
#include "OpenXRExpansionTypes.h"
#include "Engine/DataAsset.h"
#include "OpenXRHandPoseComponent.generated.h"
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|OpenXR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPXRSkeletalRepContainer
{
GENERATED_BODY()
public:
UPROPERTY(Transient, NotReplicated)
EVRSkeletalHandIndex TargetHand;
UPROPERTY(Transient, NotReplicated)
bool bAllowDeformingMesh;
// If true we will skip sending the 4 metacarpal bones that ue4 doesn't need, (STEAMVR skeletons need this disabled!)
// Only really used for the older UE4 skeleton
UPROPERTY(Transient, NotReplicated)
bool bEnableUE4HandRepSavings;
UPROPERTY(Transient, NotReplicated)
TArray<FTransform> SkeletalTransforms;
UPROPERTY(Transient, NotReplicated)
uint8 BoneCount;
FBPXRSkeletalRepContainer()
{
TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Left;
bAllowDeformingMesh = false;
bEnableUE4HandRepSavings = false;
BoneCount = 0;
}
bool bHasValidData()
{
return SkeletalTransforms.Num() > 0;
}
void CopyForReplication(FBPOpenXRActionSkeletalData& Other);
static void CopyReplicatedTo(const FBPXRSkeletalRepContainer& Container, FBPOpenXRActionSkeletalData& Other);
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
};
template<>
struct TStructOpsTypeTraits< FBPXRSkeletalRepContainer > : public TStructOpsTypeTraitsBase2<FBPXRSkeletalRepContainer>
{
enum
{
WithNetSerializer = true
};
};
USTRUCT(BlueprintType, Category = "VRGestures")
struct OPENXREXPANSIONPLUGIN_API FOpenXRGestureFingerPosition
{
GENERATED_BODY()
public:
// The Finger index, not editable
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "VRGesture")
EXRHandJointType IndexType;
// The locational value of this element 0.f - 1.f
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
FVector Value;
// The threshold within which this finger value will be detected as matching (1.0 would be always matching, IE: finger doesn't count)
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture", meta = (ClampMin = "0.0", ClampMax = "100.0", UIMin = "0.0", UIMax = "100.0"))
float Threshold;
FOpenXRGestureFingerPosition(FVector TipLoc, EXRHandJointType Type)
{
IndexType = Type;
Value = TipLoc;
Threshold = 5.0f;
}
FOpenXRGestureFingerPosition()
{
IndexType = EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT;
Value = FVector(0.f);
Threshold = 5.0f;
}
};
USTRUCT(BlueprintType, Category = "VRGestures")
struct OPENXREXPANSIONPLUGIN_API FOpenXRGesture
{
GENERATED_BODY()
public:
// Name of the recorded gesture
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
FName Name;
// Samples in the recorded gesture
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
TArray<FOpenXRGestureFingerPosition> FingerValues;
FOpenXRGesture()
{
InitPoseValues();
Name = NAME_None;
}
void InitPoseValues()
{
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT));
}
};
/**
* Items Database DataAsset, here we can save all of our game items
*/
UCLASS(BlueprintType, Category = "VRGestures")
class OPENXREXPANSIONPLUGIN_API UOpenXRGestureDatabase : public UDataAsset
{
GENERATED_BODY()
public:
// Gestures in this database
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
TArray <FOpenXRGesture> Gestures;
UOpenXRGestureDatabase()
{
}
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOpenXRGestureDetected, const FName &, GestureDetected, int32, GestureIndex, EVRSkeletalHandIndex, ActionHandType);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOpenXRGestureEnded, const FName &, GestureEnded, int32, GestureIndex, EVRSkeletalHandIndex, ActionHandType);
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent))
class OPENXREXPANSIONPLUGIN_API UOpenXRHandPoseComponent : public UActorComponent
{
GENERATED_BODY()
public:
UOpenXRHandPoseComponent(const FObjectInitializer& ObjectInitializer);
// Says whether we should run gesture detection
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
bool bDetectGestures;
UFUNCTION(BlueprintCallable, Category = "VRGestures")
void SetDetectGestures(bool bNewDetectGestures)
{
bDetectGestures = bNewDetectGestures;
}
UPROPERTY(BlueprintAssignable, Category = "VRGestures")
FOpenXRGestureDetected OnNewGestureDetected;
UPROPERTY(BlueprintAssignable, Category = "VRGestures")
FOpenXRGestureEnded OnGestureEnded;
// Known sequences
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
UOpenXRGestureDatabase *GesturesDB;
UFUNCTION(BlueprintCallable, Category = "VRGestures")
bool SaveCurrentPose(FName RecordingName, EVRSkeletalHandIndex HandToSave = EVRSkeletalHandIndex::EActionHandIndex_Right);
UFUNCTION(BlueprintCallable, Category = "VRGestures", meta = (DisplayName = "DetectCurrentPose"))
bool K2_DetectCurrentPose(UPARAM(ref) FBPOpenXRActionSkeletalData& SkeletalAction, FOpenXRGesture & GestureOut);
// This version throws events
bool DetectCurrentPose(FBPOpenXRActionSkeletalData& SkeletalAction);
// Need this as I can't think of another way for an actor component to make sure it isn't on the server
inline bool IsLocallyControlled() const
{
#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 22)
const AActor* MyOwner = GetOwner();
return MyOwner->HasLocalNetOwner();
#else
// I like epics new authority check more than mine
const AActor* MyOwner = GetOwner();
const APawn* MyPawn = Cast<APawn>(MyOwner);
return MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner && MyOwner->GetLocalRole() == ENetRole::ROLE_Authority);
#endif
}
// Using tick and not timers because skeletal components tick anyway, kind of a waste to make another tick by adding a timer over that
void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
//virtual void OnUnregister() override;
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SkeletalData|Actions")
bool bGetMockUpPoseForDebugging;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SkeletalData|Actions")
TArray<FBPOpenXRActionSkeletalData> HandSkeletalActions;
UPROPERTY(Replicated, Transient, ReplicatedUsing = OnRep_SkeletalTransformLeft)
FBPXRSkeletalRepContainer LeftHandRep;
UPROPERTY(Replicated, Transient, ReplicatedUsing = OnRep_SkeletalTransformRight)
FBPXRSkeletalRepContainer RightHandRep;
UFUNCTION(Unreliable, Server, WithValidation)
void Server_SendSkeletalTransforms(const FBPXRSkeletalRepContainer& SkeletalInfo);
bool bLerpingPositionLeft;
bool bLerpingPositionRight;
struct FTransformLerpManager
{
bool bReplicatedOnce;
bool bLerping;
float UpdateCount;
float UpdateRate;
TArray<FTransform> OldTransforms;
TArray<FTransform> NewTransforms;
FTransformLerpManager();
void PreCopyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing);
void NotifyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing);
FORCEINLINE void BlendBone(uint8 BoneToBlend, FBPOpenXRActionSkeletalData& ActionInfo, float& LerpVal, bool bExponentialSmoothing)
{
ActionInfo.SkeletalTransforms[BoneToBlend].Blend(OldTransforms[BoneToBlend], NewTransforms[BoneToBlend], LerpVal);
if (bExponentialSmoothing)
{
// Saving base back out for exponential
OldTransforms[BoneToBlend] = ActionInfo.SkeletalTransforms[BoneToBlend];
}
}
void UpdateManager(float DeltaTime, FBPOpenXRActionSkeletalData& ActionInfo, UOpenXRHandPoseComponent * ParentComp);
};
FTransformLerpManager LeftHandRepManager;
FTransformLerpManager RightHandRepManager;
UFUNCTION()
virtual void OnRep_SkeletalTransformLeft()
{
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandSkeletalActions[i].TargetHand == LeftHandRep.TargetHand)
{
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(LeftHandRep, HandSkeletalActions[i]);
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
break;
}
}
}
UFUNCTION()
virtual void OnRep_SkeletalTransformRight()
{
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandSkeletalActions[i].TargetHand == RightHandRep.TargetHand)
{
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(RightHandRep, HandSkeletalActions[i]);
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
break;
}
}
}
// If we should replicate the skeletal transform data
UPROPERTY(EditAnywhere, Category = SkeletalData)
bool bReplicateSkeletalData;
// If true we will lerp between updates of the skeletal mesh transforms and smooth the result
UPROPERTY(EditAnywhere, Category = SkeletalData)
bool bSmoothReplicatedSkeletalData;
// If true then we will use exponential smoothing with buffered correction
UPROPERTY(EditAnywhere, Category = "SkeletalData", meta = (editcondition = "bSmoothReplicatedSkeletalData"))
bool bUseExponentialSmoothing = false;
// Timestep of smoothing translation
UPROPERTY(EditAnywhere, Category = "SkeletalData", meta = (editcondition = "bUseExponentialSmoothing"))
float InterpolationSpeed = 25.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = SkeletalData)
float ReplicationRateForSkeletalAnimations;
// Used in Tick() to accumulate before sending updates, didn't want to use a timer in this case, also used for remotes to lerp position
float SkeletalNetUpdateCount;
// Used in Tick() to accumulate before sending updates, didn't want to use a timer in this case, also used for remotes to lerp position
float SkeletalUpdateCount;
};
USTRUCT()
struct OPENXREXPANSIONPLUGIN_API FOpenXRAnimInstanceProxy : public FAnimInstanceProxy
{
public:
GENERATED_BODY()
FOpenXRAnimInstanceProxy() {}
FOpenXRAnimInstanceProxy(UAnimInstance* InAnimInstance);
/** Called before update so we can copy any data we need */
virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override;
public:
EVRSkeletalHandIndex TargetHand;
TArray<FBPOpenXRActionSkeletalData> HandSkeletalActionData;
};
UCLASS(transient, Blueprintable, hideCategories = AnimInstance, BlueprintType)
class OPENXREXPANSIONPLUGIN_API UOpenXRAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
UPROPERTY(transient)
UOpenXRHandPoseComponent * OwningPoseComp;
FOpenXRAnimInstanceProxy AnimInstanceProxy;
virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override
{
return new FOpenXRAnimInstanceProxy(this);
//return &AnimInstanceProxy;
}
virtual void NativeBeginPlay() override;
//virtual void NativeInitializeAnimation() override;
UFUNCTION(BlueprintCallable, Category = "BoneMappings")
void InitializeCustomBoneMapping(UPARAM(ref) FBPOpenXRSkeletalMappingData & SkeletalMappingData);
};