improved virtual hand model animation

This commit is contained in:
Mamadou Babaei
2023-03-01 22:17:31 +01:00
parent 092b882888
commit a315bed629
4 changed files with 271 additions and 127 deletions
@@ -48,41 +48,6 @@
#include "SGCore/SGHandPose.h"
#include "SGLog/SGLog.h"
const FName& FSGVirtualHandAnimInstanceProxy::GetLeftHandBoneName(const int32 Finger, const int32 Joint)
{
static const TArray<TArray<FName>> BoneNames{
{FName(TEXT("thumb_01_l")), FName(TEXT("thumb_02_l")), FName(TEXT("thumb_03_l"))},
{FName(TEXT("index_01_l")), FName(TEXT("index_02_l")), FName(TEXT("index_03_l"))},
{FName(TEXT("middle_01_l")), FName(TEXT("middle_02_l")), FName(TEXT("middle_03_l"))},
{FName(TEXT("ring_01_l")), FName(TEXT("ring_02_l")), FName(TEXT("ring_03_l"))},
{FName(TEXT("pinky_01_l")), FName(TEXT("pinky_02_l")), FName(TEXT("pinky_03_l"))},
};
ensureAlwaysMsgf(Finger < BoneNames.Num(), TEXT("Finger index is out of bound!"));
ensureAlwaysMsgf(Joint < BoneNames[Finger].Num(), TEXT("Joint index is out of bound!"));
const FName& BoneName = BoneNames[Finger][Joint];
return BoneName;
}
const FName& FSGVirtualHandAnimInstanceProxy::GetRightHandBoneName(const int32 Finger, const int32 Joint)
{
static const TArray<TArray<FName>> BoneNames{
{FName(TEXT("thumb_01_r")), FName(TEXT("thumb_02_r")), FName(TEXT("thumb_03_r"))},
{FName(TEXT("index_01_r")), FName(TEXT("index_02_r")), FName(TEXT("index_03_r"))},
{FName(TEXT("middle_01_r")), FName(TEXT("middle_02_r")), FName(TEXT("middle_03_r"))},
{FName(TEXT("ring_01_r")), FName(TEXT("ring_02_r")), FName(TEXT("ring_03_r"))},
{FName(TEXT("pinky_01_r")), FName(TEXT("pinky_02_r")), FName(TEXT("pinky_03_r"))},
};
ensureAlwaysMsgf(Finger < BoneNames.Num(), TEXT("Finger index is out of bound!"));
ensureAlwaysMsgf(Joint < BoneNames[Finger].Num(), TEXT("Joint index is out of bound!"));
const FName& BoneName = BoneNames[Finger][Joint];
return BoneName;
}
FSGVirtualHandAnimInstanceProxy::FSGVirtualHandAnimInstanceProxy()
: FAnimInstanceProxy()
{
@@ -128,44 +93,8 @@ void FSGVirtualHandAnimInstanceProxy::PreEvaluateAnimation(UAnimInstance* InAnim
return;
}
const USGHandPose* HandPose = Hand->GetHandPose();
if (!IsValid(HandPose))
{
return;
}
BoneMap.Empty();
const TArray<TArray<FRotator>> JointRotations = HandPose->GetJointRotations();
for (int32 FingerIndex = 0; FingerIndex < JointRotations.Num(); ++FingerIndex)
{
const TArray<FRotator>& FingerJointRotations = JointRotations[FingerIndex];
for (int32 JointIndex = 0; JointIndex < FingerJointRotations.Num(); ++JointIndex)
{
// FIXME ?
// Ignore the 4th joint.
if (JointIndex >= 3)
{
continue;
}
FRotator JointRotation{FingerJointRotations[JointIndex]};
/*const FRotator JointRotation{FingerJointRotations[JointIndex]};
const FQuat JointQuat{JointRotation.Quaternion()};
const FRotator ReferenceRotation{Hand->GetActorRotation()};
const FQuat ReferenceQuat{ReferenceRotation.Quaternion()};
FRotator BoneRotation{ReferenceQuat * JointQuat};*/
FName BoneName(Hand->IsRight()
? GetRightHandBoneName(FingerIndex, JointIndex)
: GetLeftHandBoneName(FingerIndex, JointIndex));
//BoneMap.Emplace(MoveTemp(BoneName), MoveTemp(BoneRotation));
BoneMap.Emplace(MoveTemp(BoneName), MoveTemp(JointRotation));
}
}
BonesRotations.Empty();
BonesRotations = Hand->GetFingersBonesRotations();
}
bool FSGVirtualHandAnimInstanceProxy::Evaluate(FPoseContext& Output)
@@ -197,7 +126,7 @@ bool FSGVirtualHandAnimInstanceProxy::Evaluate(FPoseContext& Output)
FComponentSpacePoseContext PoseContext(this);
PoseContext.Pose.InitPose(Output.Pose);
for (const TTuple<FName, FRotator>& Bone: BoneMap)
for (const TTuple<FName, FRotator>& Bone: BonesRotations)
{
FName BoneName(Bone.Key);
FRotator BoneRotation(Bone.Value);
@@ -213,8 +142,7 @@ bool FSGVirtualHandAnimInstanceProxy::Evaluate(FPoseContext& Output)
FAnimNode_ModifyBone BoneController;
BoneController.BoneToModify = MoveTemp(BoneReference);
BoneController.RotationMode = EBoneModificationMode::BMM_Replace;
//BoneController.RotationSpace = EBoneControlSpace::BCS_WorldSpace;
BoneController.RotationSpace = EBoneControlSpace::BCS_ComponentSpace;
BoneController.RotationSpace = EBoneControlSpace::BCS_ParentBoneSpace;
BoneController.Rotation = MoveTemp(BoneRotation);
if (BoneController.IsValidToEvaluate(HandSkeleton, PoseContext.Pose.GetPose().GetBoneContainer()))
@@ -229,17 +157,8 @@ bool FSGVirtualHandAnimInstanceProxy::Evaluate(FPoseContext& Output)
BoneController.BoneToModify.GetCompactPoseIndex(
PoseContext.Pose.GetPose().GetBoneContainer())
};
const FTransform BoneTransformBefore{PoseContext.Pose.GetComponentSpaceTransform(PoseBoneIndex)};
PoseContext.Pose.LocalBlendCSBoneTransforms(BoneTransforms, 1.0f);
const FTransform BoneTransformAfter{PoseContext.Pose.GetComponentSpaceTransform(PoseBoneIndex)};
SGLOG("BoneController.Rotation", BoneController.Rotation);
SGLOG("BoneTransforms[0].Transform.GetRotation()", BoneTransforms[0].Transform.GetRotation().Rotator());
SGLOG("BoneTransformBefore.Rotation", BoneTransformBefore.Rotator());
SGLOG("BoneTransformAfter.Rotation", BoneTransformAfter.Rotator());
// Output.Pose[PoseBoneIndex].SetRotation(
// PoseContext.Pose.GetComponentSpaceTransform(PoseBoneIndex).GetRotation());
PoseContext.Pose.ConvertComponentPosesToLocalPoses(PoseContext.Pose, Output.Pose);
HandMeshComponent->MarkRenderStateDirty();
@@ -79,6 +79,12 @@ struct ASGVirtualHand::FImpl
return Path;
}
/************************
* Member variables
************************/
TMap<FName, FTransform> InitialBonesTransforms;
/************************
* Owner object
************************/
@@ -105,16 +111,64 @@ struct ASGVirtualHand::FImpl
bool IsEditor() const;
void SetHandMesh(USkeletalMesh* VirtualHandMesh, bool bReinitPos) const;
void SetHandMesh(USkeletalMesh* VirtualHandMesh, bool bReinitPos);
FString GetDefaultMeshPath() const;
bool IsUsingUserMesh() const;
USkeletalMesh* LoadDefaultMesh() const;
void SetDefaultMesh() const;
void SetDefaultMesh();
void CacheInitialBonesTransforms();
bool CheckGlove() const;
};
const TArray<TArray<FName>>& ASGVirtualHand::GetLeftHandFingerBoneNames()
{
static const TArray<TArray<FName>> BoneNames{
{FName(TEXT("thumb_01_l")), FName(TEXT("thumb_02_l")), FName(TEXT("thumb_03_l"))},
{FName(TEXT("index_01_l")), FName(TEXT("index_02_l")), FName(TEXT("index_03_l"))},
{FName(TEXT("middle_01_l")), FName(TEXT("middle_02_l")), FName(TEXT("middle_03_l"))},
{FName(TEXT("ring_01_l")), FName(TEXT("ring_02_l")), FName(TEXT("ring_03_l"))},
{FName(TEXT("pinky_01_l")), FName(TEXT("pinky_02_l")), FName(TEXT("pinky_03_l"))},
};
return BoneNames;
}
const TArray<TArray<FName>>& ASGVirtualHand::GetRightHandFingerBoneNames()
{
static const TArray<TArray<FName>> BoneNames{
{FName(TEXT("thumb_01_r")), FName(TEXT("thumb_02_r")), FName(TEXT("thumb_03_r"))},
{FName(TEXT("index_01_r")), FName(TEXT("index_02_r")), FName(TEXT("index_03_r"))},
{FName(TEXT("middle_01_r")), FName(TEXT("middle_02_r")), FName(TEXT("middle_03_r"))},
{FName(TEXT("ring_01_r")), FName(TEXT("ring_02_r")), FName(TEXT("ring_03_r"))},
{FName(TEXT("pinky_01_r")), FName(TEXT("pinky_02_r")), FName(TEXT("pinky_03_r"))},
};
return BoneNames;
}
const FName& ASGVirtualHand::GetLeftHandFingerBoneName(const int32 Finger, const int32 Joint)
{
const TArray<TArray<FName>>& BoneNames{GetLeftHandFingerBoneNames()};
ensureAlwaysMsgf(Finger < BoneNames.Num(), TEXT("Finger index is out of bound!"));
ensureAlwaysMsgf(Joint < BoneNames[Finger].Num(), TEXT("Joint index is out of bound!"));
const FName& BoneName{BoneNames[Finger][Joint]};
return BoneName;
}
const FName& ASGVirtualHand::GetRightHandFingerBoneName(const int32 Finger, const int32 Joint)
{
const TArray<TArray<FName>>& BoneNames{GetRightHandFingerBoneNames()};
ensureAlwaysMsgf(Finger < BoneNames.Num(), TEXT("Finger index is out of bound!"));
ensureAlwaysMsgf(Joint < BoneNames[Finger].Num(), TEXT("Joint index is out of bound!"));
const FName& BoneName{BoneNames[Finger][Joint]};
return BoneName;
}
ASGVirtualHand::ASGVirtualHand(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer),
#if ENGINE_MAJOR_VERSION == 5 || ( ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION > 24 )
@@ -156,12 +210,19 @@ ASGVirtualHand::ASGVirtualHand(const FObjectInitializer& ObjectInitializer)
const FString SkeletalMeshPath(Pimpl->GetDefaultMeshPath());
const ConstructorHelpers::FObjectFinder<USkeletalMesh> SkeletalMesh(*SkeletalMeshPath);
ensureAlwaysMsgf(SkeletalMesh.Succeeded() && SkeletalMesh.Object,
TEXT("Failed to load the skeletal mesh: '%s'"), *SkeletalMeshPath);
TEXT("Failed to load the skeletal mesh: '%s'"), *SkeletalMeshPath);
Pimpl->SetHandMesh(SkeletalMesh.Object, true);
Glove = nullptr;
}
void ASGVirtualHand::BeginPlay()
{
Super::BeginPlay();
Pimpl->CacheInitialBonesTransforms();
}
void ASGVirtualHand::Tick(const float DeltaSeconds)
{
#if WITH_EDITOR
@@ -174,7 +235,7 @@ void ASGVirtualHand::Tick(const float DeltaSeconds)
Super::Tick(DeltaSeconds);
(void)Pimpl->CheckGlove();
(void) Pimpl->CheckGlove();
}
bool ASGVirtualHand::ShouldTickIfViewportsOnly() const
@@ -200,21 +261,26 @@ void ASGVirtualHand::OnConstruction(const FTransform& Transform)
{
Pimpl->SetDefaultMesh();
}
Pimpl->CacheInitialBonesTransforms();
}
void ASGVirtualHand::EditorTick(float DeltaSeconds)
{
(void)Pimpl->CheckGlove();
(void) Pimpl->CheckGlove();
}
USkeletalMesh* ASGVirtualHand::GetHandMesh() const
{
ensureAlwaysMsgf(IsValid(Mesh), TEXT("Invalid Mesh object!"));
return
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 1
Mesh->GetSkeletalMeshAsset();
Mesh->GetSkeletalMeshAsset()
#else
Mesh->SkeletalMesh;
Mesh->SkeletalMesh
#endif /* ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1 */
;
}
void ASGVirtualHand::SetHandMesh(USkeletalMesh* VirtualHandMesh, const bool bReinitPos) const
@@ -222,6 +288,52 @@ void ASGVirtualHand::SetHandMesh(USkeletalMesh* VirtualHandMesh, const bool bRei
Pimpl->SetHandMesh(VirtualHandMesh, bReinitPos);
}
USkeleton* ASGVirtualHand::GetHandSkeleton() const
{
ensureAlwaysMsgf(IsValid(Mesh), TEXT("Invalid Mesh object!"));
USkeletalMesh* SkeletalMesh =
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 1
Mesh->GetSkeletalMeshAsset()
#else
Mesh->SkeletalMesh
#endif /* ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1 */
;
ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh object!"));
return
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 1
SkeletalMesh->GetSkeleton()
#else
SkeletalMesh->Skeleton
#endif /* ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1 */
;
}
const FReferenceSkeleton& ASGVirtualHand::GetHandRefSkeleton() const
{
ensureAlwaysMsgf(IsValid(Mesh), TEXT("Invalid Mesh object!"));
USkeletalMesh* SkeletalMesh =
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 1
Mesh->GetSkeletalMeshAsset()
#else
Mesh->SkeletalMesh
#endif /* ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1 */
;
ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh object!"));
return
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 1
SkeletalMesh->GetRefSkeleton()
#else
SkeletalMesh->RefSkeleton
#endif /* ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1 */
;
}
bool ASGVirtualHand::IsGloveConnected() const
{
return Pimpl->CheckGlove();
@@ -239,6 +351,81 @@ USGHandPose* ASGVirtualHand::GetHandPose()
return HandPose;
}
const FName& ASGVirtualHand::GetFingerBoneName(const int32 Finger, const int32 Joint) const
{
const FName& Name{IsRight()
? GetRightHandFingerBoneName(Finger, Joint)
: GetLeftHandFingerBoneName(Finger, Joint)};
return Name;
}
const TMap<FName, FTransform>& ASGVirtualHand::GetInitialBonesTransforms() const
{
return Pimpl->InitialBonesTransforms;
}
bool ASGVirtualHand::GetInitialBoneTransform(const int32 Finger, const int32 Joint,
FTransform& OutTransform) const
{
const FName& BoneName{GetFingerBoneName(Finger, Joint)};
const uint32 Hash = TDefaultMapHashableKeyFuncs<FName, FTransform, false>::GetKeyHash(BoneName);
FTransform* Transform{Pimpl->InitialBonesTransforms.FindByHash(Hash, BoneName)};
if (!Transform)
{
return false;
}
OutTransform = MoveTemp(*Transform);
return true;
}
bool ASGVirtualHand::GetInitialBoneRotation(const int32 Finger, const int32 Joint, FRotator& OutRotation) const
{
FTransform Transform;
if (!GetInitialBoneTransform(Finger, Joint, Transform))
{
return false;
}
OutRotation = Transform.GetRotation().Rotator();
return true;
}
TMap<FName, FRotator> ASGVirtualHand::GetFingersBonesRotations()
{
TMap<FName, FRotator> Rotations;
const USGHandPose* HandPose = GetHandPose();
if (!IsValid(HandPose))
{
return MoveTemp(Rotations);
}
const TArray<TArray<FVector>> HandAngles = HandPose->GetHandAngles();
for (int32 FingerIndex = 0; FingerIndex < HandAngles.Num(); ++FingerIndex)
{
const TArray<FVector>& FingerJointAngles = HandAngles[FingerIndex];
for (int32 JointIndex = 0; JointIndex < FingerJointAngles.Num(); ++JointIndex)
{
const FVector& FingerJointAngle{FingerJointAngles[JointIndex]};
FRotator InitialBoneRotation;
if (GetInitialBoneRotation(FingerIndex, JointIndex, InitialBoneRotation))
{
FName BoneName{GetFingerBoneName(FingerIndex, JointIndex)};
FRotator BoneRotation{(InitialBoneRotation.Vector() + FingerJointAngle).Rotation()};
Rotations.Emplace(MoveTemp(BoneName), MoveTemp(BoneRotation));
}
}
}
return MoveTemp(Rotations);
}
ASGVirtualHand::FImpl::FImpl(ASGVirtualHand* InOwner)
: Owner(InOwner)
{
@@ -246,13 +433,16 @@ ASGVirtualHand::FImpl::FImpl(ASGVirtualHand* InOwner)
ASGVirtualHand::FImpl::~FImpl() = default;
void ASGVirtualHand::FImpl::SetHandMesh(USkeletalMesh* VirtualHandMesh, const bool bReinitPos) const
void ASGVirtualHand::FImpl::SetHandMesh(USkeletalMesh* VirtualHandMesh, const bool bReinitPos)
{
ensureAlwaysMsgf(IsValid(VirtualHandMesh), TEXT("Invalid VirtualHandMesh object!"));
ensureAlwaysMsgf(IsValid(Owner->Mesh), TEXT("Invalid Mesh object!"));
ensureAlwaysMsgf(IsValid(Owner->AnimInstanceClass), TEXT("Invalid AnimInstanceClass object!"));
Owner->Mesh->SetSkeletalMesh(VirtualHandMesh, bReinitPos);
CacheInitialBonesTransforms();
Owner->Mesh->SetAnimInstanceClass(Owner->AnimInstanceClass);
}
@@ -331,11 +521,26 @@ USkeletalMesh* ASGVirtualHand::FImpl::LoadDefaultMesh() const
return SkeletalMesh;
}
void ASGVirtualHand::FImpl::SetDefaultMesh() const
void ASGVirtualHand::FImpl::SetDefaultMesh()
{
SetHandMesh(LoadDefaultMesh(), true);
}
void ASGVirtualHand::FImpl::CacheInitialBonesTransforms()
{
InitialBonesTransforms.Empty();
ensureAlwaysMsgf(IsValid(Owner->Mesh), TEXT("Invalid Mesh object!"));
const int32 NumBones = Owner->Mesh->GetNumBones();
for (int32 i = 0; i < NumBones; ++i)
{
const FName BoneName{Owner->Mesh->GetBoneName(i)};
FTransform BoneTransform{Owner->Mesh->GetSocketTransform(BoneName, RTS_ParentBoneSpace)};
InitialBonesTransforms.Emplace(BoneName, MoveTemp(BoneTransform));
}
}
bool ASGVirtualHand::FImpl::CheckGlove() const
{
const bool bGloveWasPresent = IsValid(Owner->Glove);
@@ -44,45 +44,41 @@
USTRUCT()
struct SENSEGLOVE_API FSGVirtualHandAnimInstanceProxy : public FAnimInstanceProxy
{
GENERATED_USTRUCT_BODY()
GENERATED_USTRUCT_BODY()
public:
static const FName& GetLeftHandBoneName(int32 Finger, int32 Joint);
static const FName& GetRightHandBoneName(int32 Finger, int32 Joint);
TMap<FName, FRotator> BonesRotations;
public:
TMap<FName, FRotator> BoneMap;
public:
FSGVirtualHandAnimInstanceProxy();
FSGVirtualHandAnimInstanceProxy(UAnimInstance* Instance);
FSGVirtualHandAnimInstanceProxy();
FSGVirtualHandAnimInstanceProxy(UAnimInstance* Instance);
protected:
/** Called when our anim instance is being initialized */
virtual void Initialize(UAnimInstance* InAnimInstance) override;
/** Called when our anim instance is being initialized */
virtual void Initialize(UAnimInstance* InAnimInstance) override;
/** Called when our anim instance is being uninitialized */
virtual void Uninitialize(UAnimInstance* InAnimInstance) override;
/** Called when our anim instance is being uninitialized */
virtual void Uninitialize(UAnimInstance* InAnimInstance) override;
/** Called before update so we can copy any data we need */
virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override;
/** Called before update so we can copy any data we need */
virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override;
/** Update override point */
virtual void Update(float DeltaSeconds) override;
/** Update override point */
virtual void Update(float DeltaSeconds) override;
/** Called on the game thread pre-evaluate. */
virtual void PreEvaluateAnimation(UAnimInstance* InAnimInstance) override;
/** Called on the game thread pre-evaluate. */
virtual void PreEvaluateAnimation(UAnimInstance* InAnimInstance) override;
/**
* Evaluate override point
* @return true if this function is implemented, false otherwise.
* Note: the node graph will not be evaluated if this function returns true
*/
virtual bool Evaluate(FPoseContext& Output) override;
/**
* Evaluate override point
* @return true if this function is implemented, false otherwise.
* Note: the node graph will not be evaluated if this function returns true
*/
virtual bool Evaluate(FPoseContext& Output) override;
/** Called after update so we can copy any data we need */
virtual void PostUpdate(UAnimInstance* InAnimInstance) const override;
/** Called after update so we can copy any data we need */
virtual void PostUpdate(UAnimInstance* InAnimInstance) const override;
/** Called after evaluate so we can do any game thread work we need to */
virtual void PostEvaluate(UAnimInstance* InAnimInstance) override;
/** Called after evaluate so we can do any game thread work we need to */
virtual void PostEvaluate(UAnimInstance* InAnimInstance) override;
};
@@ -35,8 +35,12 @@
#pragma once
#include "Containers/Array.h"
#include "GameFramework/Actor.h"
#include "Math/Rotator.h"
#include "Math/Transform.h"
#include "Templates/UniquePtr.h"
#include "UObject/NameTypes.h"
#include "SGVirtualHand.generated.h"
@@ -53,6 +57,13 @@ class SENSEGLOVE_API ASGVirtualHand : public AActor
{
GENERATED_UCLASS_BODY()
public:
static const TArray<TArray<FName>>& GetLeftHandFingerBoneNames();
static const TArray<TArray<FName>>& GetRightHandFingerBoneNames();
static const FName& GetLeftHandFingerBoneName(int32 Finger, int32 Joint);
static const FName& GetRightHandFingerBoneName(int32 Finger, int32 Joint);
private:
struct FImpl;
@@ -68,7 +79,7 @@ private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="SenseGlove", meta=(AllowPrivateAccess="true"))
USceneComponent* SceneRoot;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="SenseGlove", meta=(AllowPrivateAccess="true"))
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="SenseGlove", meta=(AllowPrivateAccess="true"))
USkeletalMeshComponent* Mesh;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="SenseGlove", meta=(AllowPrivateAccess="true"))
@@ -145,12 +156,15 @@ protected:
AnimInstanceClass = InAnimInstanceClass;
}
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaSeconds) override;
virtual void Tick(float DeltaSeconds) override;
virtual bool ShouldTickIfViewportsOnly() const override;
virtual bool ShouldTickIfViewportsOnly() const override;
virtual void OnConstruction(const FTransform& Transform) override;
virtual void OnConstruction(const FTransform& Transform) override;
protected:
virtual void EditorTick(float DeltaSeconds);
@@ -160,7 +174,17 @@ public:
void SetHandMesh(USkeletalMesh* VirtualHandMesh, bool bReinitPos) const;
USkeleton* GetHandSkeleton() const;
const FReferenceSkeleton& GetHandRefSkeleton() const;
bool IsGloveConnected() const;
USGHandPose* GetHandPose();
public:
const FName& GetFingerBoneName(int32 Finger, int32 Joint) const;
const TMap<FName, FTransform>& GetInitialBonesTransforms() const;
bool GetInitialBoneTransform(int32 Finger, int32 Joint, FTransform& OutTransform) const;
bool GetInitialBoneRotation(int32 Finger, int32 Joint, FRotator& OutRotation) const;
TMap<FName, FRotator> GetFingersBonesRotations();
};