Switch to VRExpansionPlugin Example 2
This commit is contained in:
parent
3e6ff514ff
commit
6c02da9299
837 changed files with 78100 additions and 0 deletions
|
@ -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 ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
|
@ -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
|
||||
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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");
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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:
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
|
||||
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue