From fb68683d72d648b2ec6b73b0d85c6456e2e9f461 Mon Sep 17 00:00:00 2001 From: Mamadou Babaei Date: Fri, 10 Mar 2023 14:11:52 +0100 Subject: [PATCH] add a new component-based class for the virtual hand --- .../Components/SGVirtualHandMeshComponent.cpp | 470 ++++++++++++++++++ .../Components/SGVirtualHandMeshComponent.h | 144 ++++++ ...GVirtualHandMeshComponentKismetLibrary.cpp | 129 +++++ .../SGVirtualHandMeshComponentKismetLibrary.h | 114 +++++ 4 files changed, 857 insertions(+) create mode 100644 Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandMeshComponent.cpp create mode 100644 Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandMeshComponent.h create mode 100644 Source/SenseGloveKismet/Private/SGKismet/SGVirtualHandMeshComponentKismetLibrary.cpp create mode 100644 Source/SenseGloveKismet/Public/SGKismet/SGVirtualHandMeshComponentKismetLibrary.h diff --git a/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandMeshComponent.cpp b/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandMeshComponent.cpp new file mode 100644 index 00000000..ebb4ee56 --- /dev/null +++ b/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandMeshComponent.cpp @@ -0,0 +1,470 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2023 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SenseGlove/Components/SGVirtualHandMeshComponent.h" + +#include "Animation/Skeleton.h" +#include "Components/SkeletalMeshComponent.h" +#include "Engine/SkeletalMesh.h" +#include "UObject/ConstructorHelpers.h" + +#include "SenseGlove/Animation/SGVirtualHandAnimInstance.h" +#include "SGCore/SGHandPose.h" +#include "SGCore/SGHapticGlove.h" +#include "SGLog/SGLog.h" + +struct USGVirtualHandMeshComponent::FImpl +{ + /************************ + * Static methods + ************************/ + + FORCEINLINE static const FString& GetDefaultLeftHandMeshPath() + { + static const FString Path{TEXT("SkeletalMesh'/SenseGlove/Meshes/" + "SK_SenseGlove_VirtualHand_Left.SK_SenseGlove_VirtualHand_Left'")}; + return Path; + } + + FORCEINLINE static const FString& GetDefaultLeftHandMeshPathOnly() + { + static const FString Path{TEXT("/SenseGlove/Meshes/" + "SK_SenseGlove_VirtualHand_Left.SK_SenseGlove_VirtualHand_Left")}; + return Path; + } + + FORCEINLINE static const FString& GetDefaultRightHandMeshPath() + { + static const FString Path{TEXT("SkeletalMesh'/SenseGlove/Meshes/" + "SK_SenseGlove_VirtualHand_Right.SK_SenseGlove_VirtualHand_Right'")}; + return Path; + } + + FORCEINLINE static const FString& GetDefaultRightHandMeshPathOnly() + { + static const FString Path{TEXT("/SenseGlove/Meshes/" + "SK_SenseGlove_VirtualHand_Right.SK_SenseGlove_VirtualHand_Right")}; + return Path; + } + + /************************ + * Owner object + ************************/ + + USGVirtualHandMeshComponent* Owner; + + /************************ + * Constructor / Destructor + ************************/ + + explicit FImpl(USGVirtualHandMeshComponent* InOwner); + ~FImpl(); + + /************************ + * Default copy constructor & copy assignment operator + ************************/ + + FImpl(const FImpl& Rhs) = default; + FImpl& operator=(const FImpl& Rhs) = default; + + /************************ + * Methods + ************************/ + + bool IsEditor() const; + + FString GetDefaultMeshPath() const; + bool IsUsingUserMesh() const; + USkeletalMesh* LoadDefaultMesh() const; + void SetDefaultMesh() const; + + USkeletalMesh* GetMesh() const; + USkeleton* GetSkeleton() const; + const FReferenceSkeleton& GetRefSkeleton() const; + + bool CheckGlove() const; +}; + +const TArray>& USGVirtualHandMeshComponent::GetLeftHandFingerBoneNames() +{ + static const TArray> 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>& USGVirtualHandMeshComponent::GetRightHandFingerBoneNames() +{ + static const TArray> 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& USGVirtualHandMeshComponent::GetLeftHandFingerBoneName(const int32 Finger, const int32 Joint) +{ + const TArray>& 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& USGVirtualHandMeshComponent::GetRightHandFingerBoneName(const int32 Finger, const int32 Joint) +{ + const TArray>& 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; +} + +USGVirtualHandMeshComponent::USGVirtualHandMeshComponent(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer), + Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) +{ + PrimaryComponentTick.bTickEvenWhenPaused = false; + PrimaryComponentTick.bCanEverTick = true; + PrimaryComponentTick.bStartWithTickEnabled = true; + bTickInEditor = true; + bNeverNeedsRenderUpdate = false; + bAllowConcurrentTick = true; + + SetCastShadow(true); + SetCastHiddenShadow(false); + bCastDynamicShadow = true; + SetOnlyOwnerSee(false); + SetReceivesDecals(true); + SetCanEverAffectNavigation(false); + Super::SetCollisionObjectType(ECC_WorldDynamic); + Super::SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + Super::SetCollisionResponseToAllChannels(ECR_Overlap); + Super::SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block); + Super::SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Block); + Super::SetCollisionResponseToChannel(ECC_Pawn, ECR_Block); + Super::SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); + Super::SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Block); + Super::SetCollisionResponseToChannel(ECC_Vehicle, ECR_Block); + Super::SetCollisionResponseToChannel(ECC_Destructible, ECR_Block); + + Super::SetAnimClass(USGVirtualHandAnimInstance::StaticClass()); + + bRight = true; + bHiddenInGameIfNoGloveDetected = true; + + const FString SkeletalMeshPath(Pimpl->GetDefaultMeshPath()); + const ConstructorHelpers::FObjectFinder SkeletalMesh(*SkeletalMeshPath); + ensureAlwaysMsgf(SkeletalMesh.Succeeded() && SkeletalMesh.Object, + TEXT("Failed to load the skeletal mesh: '%s'"), *SkeletalMeshPath); + Super::SetSkeletalMesh(SkeletalMesh.Object, true); + + Glove = nullptr; +} + +void USGVirtualHandMeshComponent::SetAnimClass(UClass* NewClass) +{ + ensureAlwaysMsgf(Cast(NewClass), + TEXT("The AnimInstance class must be a subclass of USGVirtualHandAnimInstance!")); + + Super::SetAnimClass(NewClass); +} + +void USGVirtualHandMeshComponent::InitializeComponent() +{ + Super::InitializeComponent(); + + if (!Pimpl->IsUsingUserMesh()) + { + Pimpl->SetDefaultMesh(); + } +} + +void USGVirtualHandMeshComponent::TickComponent( + const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ +#if WITH_EDITOR + if (Pimpl->IsEditor()) + { + EditorTick(DeltaTime, TickType, ThisTickFunction); + return; + } +#endif + + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + (void) Pimpl->CheckGlove(); +} + +void USGVirtualHandMeshComponent::SetSkeletalMesh(USkeletalMesh* NewMesh, const bool bReinitPose) +{ + if (IsValid(NewMesh)) + { + Super::SetSkeletalMesh(NewMesh, bReinitPose); + } + else + { + Super::SetSkeletalMesh(Pimpl->LoadDefaultMesh(), bReinitPose); + } +} + +void USGVirtualHandMeshComponent::EditorTick( + const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + (void) Pimpl->CheckGlove(); +} + + +bool USGVirtualHandMeshComponent::IsGloveConnected() const +{ + return Pimpl->CheckGlove(); +} + +USGHandPose* USGVirtualHandMeshComponent::GetHandPose() +{ + USGHandPose* HandPose = nullptr; + + if (IsValid(Glove)) + { + Glove->GetHandPose(this, HandPose); + } + + return HandPose; +} + +const FName& USGVirtualHandMeshComponent::GetFingerBoneName(const int32 Finger, const int32 Joint) const +{ + const FName& Name{IsRight() + ? GetRightHandFingerBoneName(Finger, Joint) + : GetLeftHandFingerBoneName(Finger, Joint)}; + return Name; +} + +const FTransform& USGVirtualHandMeshComponent::GetFingerBoneRefTransform(const int32 Finger, const int32 Joint) const +{ + const FName& BoneName{GetFingerBoneName(Finger, Joint)}; + const FReferenceSkeleton& ReferenceSkeleton{Pimpl->GetRefSkeleton()}; + const int32 BoneIndex = ReferenceSkeleton.FindBoneIndex(BoneName); + + ensureAlwaysMsgf(BoneIndex != INDEX_NONE, TEXT("Invalid virtual hand bone name '%s' or index!"), + *BoneName.ToString()); + + const FTransform& BoneTransform{ReferenceSkeleton.GetRefBonePose()[BoneIndex]}; + return BoneTransform; +} + +FRotator USGVirtualHandMeshComponent::GetFingerBoneRefRotation(const int32 Finger, const int32 Joint) const +{ + const FTransform BoneTransform{GetFingerBoneRefTransform(Finger, Joint)}; + FRotator BoneRotation{BoneTransform.GetRotation().Rotator()}; + return MoveTemp(BoneRotation); +} + +TMap USGVirtualHandMeshComponent::GetFingersBonesRotations() +{ + TMap Rotations; + + const USGHandPose* HandPose = GetHandPose(); + if (!IsValid(HandPose)) + { + return MoveTemp(Rotations); + } + + const TArray> HandAngles{HandPose->GetHandAngles()}; + + for (int32 FingerIndex = 0; FingerIndex < HandAngles.Num(); ++FingerIndex) + { + const TArray& FingerJointAngles{HandAngles[FingerIndex]}; + + for (int32 JointIndex = 0; JointIndex < FingerJointAngles.Num(); ++JointIndex) + { + const FVector& JointAngle{FingerJointAngles[JointIndex]}; + const FQuat JointRotation{FQuat::MakeFromEuler(JointAngle)}; + + const FTransform& BoneRefTransform{GetFingerBoneRefTransform(FingerIndex, JointIndex)}; + + FQuat NewBoneRotation{BoneRefTransform.GetRotation() * JointRotation}; + + FName BoneName{GetFingerBoneName(FingerIndex, JointIndex)}; + + Rotations.Emplace(MoveTemp(BoneName), MoveTemp(NewBoneRotation)); + } + } + + return MoveTemp(Rotations); +} + +USGVirtualHandMeshComponent::FImpl::FImpl(USGVirtualHandMeshComponent* InOwner) + : Owner(InOwner) +{ +} + +USGVirtualHandMeshComponent::FImpl::~FImpl() = default; + +bool USGVirtualHandMeshComponent::FImpl::IsEditor() const +{ + const UWorld* World = Owner->GetWorld(); + if (World && World->WorldType == EWorldType::Editor) + { + return true; + } + + return false; +} + +FString USGVirtualHandMeshComponent::FImpl::GetDefaultMeshPath() const +{ + FString SkeletalMeshPath; + + if (Owner->bRight) + { + SkeletalMeshPath = GetDefaultRightHandMeshPath(); + } + else + { + SkeletalMeshPath = GetDefaultLeftHandMeshPath(); + } + + return MoveTemp(SkeletalMeshPath); +} + +bool USGVirtualHandMeshComponent::FImpl::IsUsingUserMesh() const +{ + const USkeletalMesh* SkeletalMesh{GetMesh()}; + if (!IsValid(SkeletalMesh)) + { + return false; + } + + const FString CurrentMeshPath(SkeletalMesh->GetPathName()); + const FString LeftHandDefaultMeshPath(GetDefaultLeftHandMeshPath()); + const FString LeftHandDefaultMeshPathOnly(GetDefaultLeftHandMeshPathOnly()); + const FString RightHandDefaultMeshPath(GetDefaultRightHandMeshPath()); + const FString RightHandDefaultMeshPathOnly(GetDefaultRightHandMeshPathOnly()); + if (CurrentMeshPath == LeftHandDefaultMeshPath + || CurrentMeshPath == LeftHandDefaultMeshPathOnly + || CurrentMeshPath == RightHandDefaultMeshPath + || CurrentMeshPath == RightHandDefaultMeshPathOnly) + { + return false; + } + + return true; +} + +USkeletalMesh* USGVirtualHandMeshComponent::FImpl::LoadDefaultMesh() const +{ + const FString SkeletalMeshPath(GetDefaultMeshPath()); + USkeletalMesh* SkeletalMesh = Cast( + StaticLoadObject(USkeletalMesh::StaticClass(), nullptr, *SkeletalMeshPath)); + + ensureAlwaysMsgf(SkeletalMesh, TEXT("Failed to load the skeletal mesh: '%s'"), *SkeletalMeshPath); + + return SkeletalMesh; +} + +void USGVirtualHandMeshComponent::FImpl::SetDefaultMesh() const +{ + Owner->SetSkeletalMesh(LoadDefaultMesh(), true); +} + +USkeletalMesh* USGVirtualHandMeshComponent::FImpl::GetMesh() const +{ + return Owner-> +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 1 + GetSkeletalMeshAsset() +#else + SkeletalMesh +#endif /* ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1 */ + ; +} + +USkeleton* USGVirtualHandMeshComponent::FImpl::GetSkeleton() const +{ + USkeletalMesh* SkeletalMesh{GetMesh()}; + ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh object!")); + + return SkeletalMesh-> +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 1 + GetSkeleton() +#else + Skeleton +#endif /* ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1 */ + ; +} + +const FReferenceSkeleton& USGVirtualHandMeshComponent::FImpl::GetRefSkeleton() const +{ + USkeletalMesh* SkeletalMesh{GetMesh()}; + ensureAlwaysMsgf(IsValid(SkeletalMesh), TEXT("Invalid SkeletalMesh object!")); + + return SkeletalMesh-> +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 1 + GetRefSkeleton() +#else + RefSkeleton +#endif /* ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1 */ + ; +} + +bool USGVirtualHandMeshComponent::FImpl::CheckGlove() const +{ + const bool bGloveWasPresent = IsValid(Owner->Glove); + const bool bFoundGlove = USGHapticGlove::GetGlove(Owner, Owner->IsRight(), Owner->Glove); + + if (!bFoundGlove && bGloveWasPresent) + { + SGLOG_ERROR(FString::Printf(TEXT("Could not find a %s glove"), + (Owner->bRight ? TEXT("right-handed") : TEXT("left-handed")))); + } + + return bFoundGlove; +} + +void USGVirtualHandMeshComponent::FImplDeleter::operator()(const FImpl* P) const +{ + delete P; +} \ No newline at end of file diff --git a/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandMeshComponent.h b/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandMeshComponent.h new file mode 100644 index 00000000..be0f285f --- /dev/null +++ b/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandMeshComponent.h @@ -0,0 +1,144 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2023 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "Containers/Array.h" +#include "Components/SkeletalMeshComponent.h" +#include "Containers/Map.h" +#include "GameFramework/Actor.h" +#include "Math/Rotator.h" +#include "Math/Transform.h" +#include "ReferenceSkeleton.h" +#include "Templates/UniquePtr.h" +#include "UObject/NameTypes.h" + +#include "SGVirtualHandMeshComponent.generated.h" + +class USkeletalMesh; +class USkeleton; +class USceneComponent; + +class USGHandPose; +class USGHapticGlove; + +UCLASS(Config=Engine, Blueprintable, ClassGroup=(Rendering, Common), hidecategories=(Object, "Mesh|SkeletalAsset"), + editinlinenew, meta=(BlueprintSpawnableComponent)) +class SENSEGLOVE_API USGVirtualHandMeshComponent : public USkeletalMeshComponent +{ + GENERATED_UCLASS_BODY() + +public: + static const TArray>& GetLeftHandFingerBoneNames(); + static const TArray>& GetRightHandFingerBoneNames(); + + static const FName& GetLeftHandFingerBoneName(int32 Finger, int32 Joint); + static const FName& GetRightHandFingerBoneName(int32 Finger, int32 Joint); + +private: + struct FImpl; + + struct FImplDeleter + { + void operator()(const FImpl* P) const; + }; + + TUniquePtr Pimpl; + FImplDeleter PimplDeleter; + +private: + UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false")) + uint8 bRight : 1; + + UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false")) + uint8 bHiddenInGameIfNoGloveDetected : 1; + +private: + UPROPERTY(Transient) + USGHapticGlove* Glove; + +public: + FORCEINLINE bool IsLeft() const + { + return !IsRight(); + } + + FORCEINLINE bool IsRight() const + { + return !!bRight; + } + + FORCEINLINE void SetRight(const bool bInRight) + { + bRight = bInRight; + } + + FORCEINLINE bool IsHiddenInGameIfNoGloveDetected() const + { + return !!bHiddenInGameIfNoGloveDetected; + } + + FORCEINLINE void SetHiddenIfNoGloveDetected(const bool bInHiddenIfNoGloveDetected) + { + bHiddenInGameIfNoGloveDetected = bInHiddenIfNoGloveDetected; + } + +public: + virtual void SetAnimClass(class UClass* NewClass) override; + + virtual void InitializeComponent() override; + + virtual void TickComponent(float DeltaTime, + enum ELevelTick TickType, + FActorComponentTickFunction* ThisTickFunction) override; + + virtual void SetSkeletalMesh(USkeletalMesh* NewMesh, bool bReinitPose = true) override; + +protected: + virtual void EditorTick(float DeltaTime, + enum ELevelTick TickType, + FActorComponentTickFunction* ThisTickFunction); + +public: + bool IsGloveConnected() const; + USGHandPose* GetHandPose(); + +public: + const FName& GetFingerBoneName(int32 Finger, int32 Joint) const; + const FTransform& GetFingerBoneRefTransform(int32 Finger, int32 Joint) const; + + FRotator GetFingerBoneRefRotation(int32 Finger, int32 Joint) const; + TMap GetFingersBonesRotations(); +}; \ No newline at end of file diff --git a/Source/SenseGloveKismet/Private/SGKismet/SGVirtualHandMeshComponentKismetLibrary.cpp b/Source/SenseGloveKismet/Private/SGKismet/SGVirtualHandMeshComponentKismetLibrary.cpp new file mode 100644 index 00000000..ce326fed --- /dev/null +++ b/Source/SenseGloveKismet/Private/SGKismet/SGVirtualHandMeshComponentKismetLibrary.cpp @@ -0,0 +1,129 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2023 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGKismet/SGVirtualHandMeshComponentKismetLibrary.h" + +#include "SenseGlove/Components/SGVirtualHandMeshComponent.h" +#include "SGCore/SGHandPose.h" +#include "SGUtils/SGArrayUtils.h" + +TArray UVirtualHandMeshComponentKismetLibrary::GetLeftHandFingerBoneNames() +{ + return FSGArrayUtils::ToNestedBPArray( + USGVirtualHandMeshComponent::GetLeftHandFingerBoneNames()); +} + +TArray UVirtualHandMeshComponentKismetLibrary::GetRightHandFingerBoneNames() +{ + return FSGArrayUtils::ToNestedBPArray( + USGVirtualHandMeshComponent::GetRightHandFingerBoneNames()); +} + +const FName& UVirtualHandMeshComponentKismetLibrary::GetLeftHandFingerBoneName(const int32 Finger, const int32 Joint) +{ + return USGVirtualHandMeshComponent::GetLeftHandFingerBoneName(Finger, Joint); +} + +const FName& UVirtualHandMeshComponentKismetLibrary::GetRightHandFingerBoneName(const int32 Finger, const int32 Joint) +{ + return USGVirtualHandMeshComponent::GetRightHandFingerBoneName(Finger, Joint); +} + +bool UVirtualHandMeshComponentKismetLibrary::IsLeft(const USGVirtualHandMeshComponent* VirtualHandMeshComponent) +{ + return VirtualHandMeshComponent->IsLeft(); +} + +bool UVirtualHandMeshComponentKismetLibrary::IsRight(const USGVirtualHandMeshComponent* VirtualHandMeshComponent) +{ + return VirtualHandMeshComponent->IsRight(); +} + +void UVirtualHandMeshComponentKismetLibrary::SetRight(USGVirtualHandMeshComponent* VirtualHandMeshComponent, + const bool bInRight) +{ + VirtualHandMeshComponent->SetRight(bInRight); +} + +bool UVirtualHandMeshComponentKismetLibrary::IsHiddenInGameIfNoGloveDetected( + const USGVirtualHandMeshComponent* VirtualHandMeshComponent) +{ + return VirtualHandMeshComponent->IsHiddenInGameIfNoGloveDetected(); +} + +void UVirtualHandMeshComponentKismetLibrary::SetHiddenIfNoGloveDetected( + USGVirtualHandMeshComponent* VirtualHandMeshComponent, + const bool bInHiddenIfNoGloveDetected) +{ + VirtualHandMeshComponent->SetHiddenIfNoGloveDetected(bInHiddenIfNoGloveDetected); +} + +bool UVirtualHandMeshComponentKismetLibrary::IsGloveConnected( + const USGVirtualHandMeshComponent* VirtualHandMeshComponent) const +{ + return VirtualHandMeshComponent->IsGloveConnected(); +} + +USGHandPose* UVirtualHandMeshComponentKismetLibrary::GetHandPose(USGVirtualHandMeshComponent* VirtualHandMeshComponent) +{ + return VirtualHandMeshComponent->GetHandPose(); +} + +const FName& UVirtualHandMeshComponentKismetLibrary::GetFingerBoneName( + const USGVirtualHandMeshComponent* VirtualHandMeshComponent, + const int32 Finger, const int32 Joint) const +{ + return VirtualHandMeshComponent->GetFingerBoneName(Finger, Joint); +} + +const FTransform& UVirtualHandMeshComponentKismetLibrary::GetFingerBoneRefTransform( + const USGVirtualHandMeshComponent* VirtualHandMeshComponent, + const int32 Finger, const int32 Joint) const +{ + return VirtualHandMeshComponent->GetFingerBoneRefTransform(Finger, Joint); +} + +FRotator UVirtualHandMeshComponentKismetLibrary::GetFingerBoneRefRotation( + const USGVirtualHandMeshComponent* VirtualHandMeshComponent, + const int32 Finger, const int32 Joint) const +{ + return VirtualHandMeshComponent->GetFingerBoneRefRotation(Finger, Joint); +} + +TMap UVirtualHandMeshComponentKismetLibrary::GetFingersBonesRotations( + USGVirtualHandMeshComponent* VirtualHandMeshComponent) +{ + return VirtualHandMeshComponent->GetFingersBonesRotations(); +} \ No newline at end of file diff --git a/Source/SenseGloveKismet/Public/SGKismet/SGVirtualHandMeshComponentKismetLibrary.h b/Source/SenseGloveKismet/Public/SGKismet/SGVirtualHandMeshComponentKismetLibrary.h new file mode 100644 index 00000000..24266738 --- /dev/null +++ b/Source/SenseGloveKismet/Public/SGKismet/SGVirtualHandMeshComponentKismetLibrary.h @@ -0,0 +1,114 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2023 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * A data class containing the minimum required data for a hand's forward + * kinematics. + * Used in both kinematics and calibration. + */ + + +#pragma once + +#include "Containers/Array.h" +#include "Containers/Map.h" +#include "GameFramework/Actor.h" +#include "Math/Rotator.h" +#include "Math/Transform.h" +#include "UObject/NameTypes.h" + +#include "SGKismet/SGBlueprintFunctionLibrary.h" +#include "SGTypes/SGNameArray.h" + +#include "SGVirtualHandMeshComponentKismetLibrary.generated.h" + +class USkeletalMeshComponent; + +class USGVirtualHandMeshComponent; +class USGHandPose; + +/** + * Represents data of a user's hand required for forward kinematics. + */ +UCLASS(meta=(ScriptName = "SesneGloveVirtualHandMeshComponentKismetLibrary")) +class SENSEGLOVEKISMET_API UVirtualHandMeshComponentKismetLibrary final : public USGBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Virtual Hand Mesh Component") + static TArray GetLeftHandFingerBoneNames(); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Virtual Hand Mesh Component") + static TArray GetRightHandFingerBoneNames(); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Virtual Hand Mesh Component") + static const FName& GetLeftHandFingerBoneName(int32 Finger, int32 Joint); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Virtual Hand Mesh Component") + static const FName& GetRightHandFingerBoneName(int32 Finger, int32 Joint); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Virtual Hand Mesh Component") + static bool IsLeft(const USGVirtualHandMeshComponent* VirtualHandMeshComponent); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Virtual Hand Mesh Component") + static bool IsRight(const USGVirtualHandMeshComponent* VirtualHandMeshComponent); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Virtual Hand Mesh Component") + static void SetRight(UPARAM(ref) USGVirtualHandMeshComponent* VirtualHandMeshComponent, bool bInRight); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Virtual Hand Mesh Component") + static bool IsHiddenInGameIfNoGloveDetected(const USGVirtualHandMeshComponent* VirtualHandMeshComponent); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Virtual Hand Mesh Component") + static void SetHiddenIfNoGloveDetected(UPARAM(ref) USGVirtualHandMeshComponent* VirtualHandMeshComponent, + bool bInHiddenIfNoGloveDetected); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Virtual Hand Mesh Component") + bool IsGloveConnected(const USGVirtualHandMeshComponent* VirtualHandMeshComponent) const; + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Virtual Hand Mesh Component") + USGHandPose* GetHandPose(UPARAM(ref) USGVirtualHandMeshComponent* VirtualHandMeshComponent); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Virtual Hand Mesh Component") + const FName& GetFingerBoneName(const USGVirtualHandMeshComponent* VirtualHandMeshComponent, + int32 Finger, int32 Joint) const; + + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Virtual Hand Mesh Component") + const FTransform& GetFingerBoneRefTransform(const USGVirtualHandMeshComponent* VirtualHandMeshComponent, + int32 Finger, int32 Joint) const; + + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Virtual Hand Mesh Component") + FRotator GetFingerBoneRefRotation(const USGVirtualHandMeshComponent* VirtualHandMeshComponent, + int32 Finger, int32 Joint) const; + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Virtual Hand Mesh Component") + TMap GetFingersBonesRotations(UPARAM(ref) USGVirtualHandMeshComponent* VirtualHandMeshComponent); +}; \ No newline at end of file