898 lines
28 KiB
C++
898 lines
28 KiB
C++
/**
|
|
* @file
|
|
*
|
|
* @author Mamadou Babaei <mamadou@senseglove.com>
|
|
*
|
|
* @section LICENSE
|
|
*
|
|
* (The MIT License)
|
|
*
|
|
* Copyright (c) 2020 - 2026 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/SGVirtualHandComponent.h"
|
|
|
|
#include "HeadMountedDisplayTypes.h"
|
|
#include "Animation/Skeleton.h"
|
|
#include "Components/SkeletalMeshComponent.h"
|
|
#include "Engine/Engine.h"
|
|
#include "Engine/EngineTypes.h"
|
|
#include "Engine/SkeletalMesh.h"
|
|
#include "InputCoreTypes.h"
|
|
#include "Templates/UnrealTypeTraits.h"
|
|
#include "UObject/ConstructorHelpers.h"
|
|
|
|
#include "SenseGlove/Animation/SGVirtualHandAnimInstance.h"
|
|
#include "SenseGlove/Components/SGWristTrackerComponent.h"
|
|
#include "SGCore/SGHapticGlove.h"
|
|
#include "SGDebug/SGDebugVirtualHand.h"
|
|
#include "SGLog/SGLog.h"
|
|
#include "SGSettings/SGSettings.h"
|
|
#include "SGTracking/SGGloveTracker.h"
|
|
#include "SGTracking/SGXRTracker.h"
|
|
|
|
struct USGVirtualHandComponent::FImpl
|
|
{
|
|
/************************
|
|
* Static methods
|
|
************************/
|
|
|
|
static FName ParseVirtualHandSettingsOverridesPropertyName(const FName& PropertyName);
|
|
|
|
FORCEINLINE static const FSGVirtualHandSettings& GetPluginVirtualHandSettings()
|
|
{
|
|
const USGSettings* Settings{USGSettings::GetInstance()};
|
|
ensureAlwaysMsgf(Settings, TEXT("The settings subsystem has not been initialized!"));
|
|
return Settings->GetVirtualHandSettings();
|
|
}
|
|
|
|
FORCEINLINE static const FString& GetDefaultLeftHandMeshPath()
|
|
{
|
|
return GetPluginVirtualHandSettings().MeshSettings.DefaultLeftHandMeshPath;
|
|
}
|
|
|
|
FORCEINLINE static const FString& GetDefaultLeftHandMeshPathOnly()
|
|
{
|
|
return GetPluginVirtualHandSettings().MeshSettings.DefaultLeftHandMeshPathOnly;
|
|
}
|
|
|
|
FORCEINLINE static const FString& GetDefaultRightHandMeshPath()
|
|
{
|
|
return GetPluginVirtualHandSettings().MeshSettings.DefaultRightHandMeshPath;
|
|
}
|
|
|
|
FORCEINLINE static const FString& GetDefaultRightHandMeshPathOnly()
|
|
{
|
|
return GetPluginVirtualHandSettings().MeshSettings.DefaultRightHandMeshPathOnly;
|
|
}
|
|
|
|
/************************
|
|
* Member variables
|
|
************************/
|
|
|
|
bool bOverridePluginSettingsPropertyAlreadyModified;
|
|
|
|
bool bCachedGenerateOverlapEvents;
|
|
ECollisionEnabled::Type CachedCollisionEnabled;
|
|
FCollisionResponseContainer CachedCollisionResponse;
|
|
|
|
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION < 7
|
|
FXRMotionControllerData MotionControllerData;
|
|
#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION < 7 */
|
|
|
|
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
|
|
FXRHandTrackingState HandTrackingState;
|
|
#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
|
|
/************************
|
|
* Owner object
|
|
************************/
|
|
|
|
USGVirtualHandComponent* Owner;
|
|
|
|
/************************
|
|
* Constructor / Destructor
|
|
************************/
|
|
|
|
explicit FImpl(USGVirtualHandComponent* InOwner);
|
|
~FImpl();
|
|
|
|
/************************
|
|
* Default copy constructor & copy assignment operator
|
|
************************/
|
|
|
|
FImpl(const FImpl& Rhs) = default;
|
|
FImpl& operator=(const FImpl& Rhs) = default;
|
|
|
|
/************************
|
|
* Methods
|
|
************************/
|
|
|
|
bool IsEditor() const;
|
|
|
|
void RightPropertyChanged();
|
|
void OverridePluginSettingsPropertyChanged();
|
|
|
|
FString GetDefaultMeshPath() const;
|
|
bool IsUsingDefaultMesh() const;
|
|
bool IsUsingUserMesh() const;
|
|
USkeletalMesh* LoadDefaultMesh() const;
|
|
void SetDefaultMesh() const;
|
|
|
|
USkeletalMesh* GetMesh() const;
|
|
USkeleton* GetSkeleton() const;
|
|
const FReferenceSkeleton& GetRefSkeleton() const;
|
|
|
|
void DisableCollisionResponse();
|
|
void RestoreCollisionResponse() const;
|
|
|
|
void UpdateXRData();
|
|
|
|
void SetVisibility(const bool bInVisible);
|
|
void UpdateVisibility();
|
|
|
|
FORCEINLINE bool ShouldDrawDebugVirtualHand() const
|
|
{
|
|
const FSGVirtualHandDebuggingSettings& Settings{Owner->GetVirtualHandSettings().DebuggingSettings};
|
|
const bool bDraw = Settings.bDrawDebugVirtualHand
|
|
&& Settings.DrawingMode != ESGDebugVirtualHandDrawingMode::None;
|
|
return bDraw;
|
|
}
|
|
|
|
void DrawDebugVirtualHand() const;
|
|
|
|
void TriggerHandVisibilityChangedEvent(bool bNewVisibility) const;
|
|
};
|
|
|
|
FName USGVirtualHandComponent::FImpl::ParseVirtualHandSettingsOverridesPropertyName(const FName& PropertyName)
|
|
{
|
|
static const UStruct* Struct{FSGVirtualHandSettingsOverrides::StaticStruct()};
|
|
if (!ensureAlwaysMsgf(Struct, TEXT("Invalid Struct!")))
|
|
{
|
|
return NAME_None;
|
|
}
|
|
|
|
// +3 because of ::
|
|
static const int32 StructNameLength = Struct->GetFName().ToString().Len() + 3;
|
|
|
|
const FString PropertyString{PropertyName.ToString()};
|
|
const int32 PropertyStringLength = PropertyString.Len();
|
|
const int32 PropertyNameLength = PropertyStringLength - StructNameLength;
|
|
|
|
const FName ParsedName{*PropertyString.Right(PropertyNameLength)};
|
|
return ParsedName;
|
|
}
|
|
|
|
const TArray<FName>& USGVirtualHandComponent::GetLeftHandBoneNames()
|
|
{
|
|
return FImpl::GetPluginVirtualHandSettings().MeshSettings.LeftHandBoneNames;
|
|
}
|
|
|
|
const TArray<FName>& USGVirtualHandComponent::GetRightHandBoneNames()
|
|
{
|
|
return FImpl::GetPluginVirtualHandSettings().MeshSettings.RightHandBoneNames;
|
|
}
|
|
|
|
const FName& USGVirtualHandComponent::GetLeftHandBoneName(const int32 Joint)
|
|
{
|
|
const TArray<FName>& BoneNames{GetLeftHandBoneNames()};
|
|
|
|
ensureAlwaysMsgf(Joint < BoneNames.Num(), TEXT("Joint index is out of bound!"));
|
|
|
|
const FName& BoneName{BoneNames[Joint]};
|
|
return BoneName;
|
|
}
|
|
|
|
const FName& USGVirtualHandComponent::GetRightHandBoneName(const int32 Joint)
|
|
{
|
|
const TArray<FName>& BoneNames{GetRightHandBoneNames()};
|
|
|
|
ensureAlwaysMsgf(Joint < BoneNames.Num(), TEXT("Joint index is out of bound!"));
|
|
|
|
const FName& BoneName{BoneNames[Joint]};
|
|
return BoneName;
|
|
}
|
|
|
|
USGVirtualHandComponent::USGVirtualHandComponent(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer),
|
|
Pimpl(TUniquePtr<FImpl, FImplDeleter>(new FImpl{this}, PimplDeleter))
|
|
{
|
|
PrimaryComponentTick.bTickEvenWhenPaused = false;
|
|
PrimaryComponentTick.bCanEverTick = true;
|
|
PrimaryComponentTick.bStartWithTickEnabled = true;
|
|
bTickInEditor = true;
|
|
bNeverNeedsRenderUpdate = false;
|
|
bAllowConcurrentTick = true;
|
|
SetUpdateAnimationInEditor(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);
|
|
|
|
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
|
|
Super::SetAnimInstanceClass(USGVirtualHandAnimInstance::StaticClass());
|
|
#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
Super::SetAnimClass(USGVirtualHandAnimInstance::StaticClass());
|
|
#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
|
|
bRight = true;
|
|
|
|
Super::SetSkeletalMesh(nullptr, true);
|
|
|
|
WristTracker = nullptr;
|
|
}
|
|
|
|
void USGVirtualHandComponent::OnHandVisibilityChanged_Implementation(bool bNewVisibility)
|
|
{
|
|
}
|
|
|
|
void USGVirtualHandComponent::SetRight(const bool bInRight)
|
|
{
|
|
bRight = bInRight;
|
|
|
|
if (!Pimpl->IsUsingUserMesh())
|
|
{
|
|
Pimpl->SetDefaultMesh();
|
|
}
|
|
}
|
|
|
|
void USGVirtualHandComponent::SetVirtualHandSettingsOverrides(
|
|
const FSGVirtualHandSettingsOverrides& InVirtualHandSettingsOverrides)
|
|
{
|
|
VirtualHandSettingsOverrides = InVirtualHandSettingsOverrides;
|
|
}
|
|
|
|
FSGVirtualHandSettings USGVirtualHandComponent::GetVirtualHandSettings() const
|
|
{
|
|
const USGSettings* Settings{USGSettings::GetInstance()};
|
|
if (!IsValid(Settings))
|
|
{
|
|
return FSGVirtualHandSettings{};
|
|
}
|
|
|
|
const FSGVirtualHandSettings& PluginVirtualHandSettings{Settings->GetVirtualHandSettings()};
|
|
if (!OverridesPluginVirtualHandSettings())
|
|
{
|
|
return PluginVirtualHandSettings;
|
|
}
|
|
|
|
const FSGVirtualHandSettings VirtualHandSettings{
|
|
VirtualHandSettingsOverrides.bVisibleWhenHandDataUnavailable,
|
|
VirtualHandSettingsOverrides.AnimationSettings,
|
|
VirtualHandSettingsOverrides.DebuggingSettings,
|
|
VirtualHandSettingsOverrides.GrabSettings,
|
|
VirtualHandSettingsOverrides.HapticsSettings,
|
|
PluginVirtualHandSettings.MeshSettings,
|
|
VirtualHandSettingsOverrides.TouchSettings
|
|
};
|
|
return VirtualHandSettings;
|
|
}
|
|
|
|
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
|
|
void USGVirtualHandComponent::SetAnimInstanceClass(UClass* NewClass)
|
|
{
|
|
ensureAlwaysMsgf((TIsDerivedFrom<decltype(NewClass), USGVirtualHandAnimInstance>::IsDerived),
|
|
TEXT("The AnimInstance class must be a subclass of USGVirtualHandAnimInstance!"));
|
|
|
|
Super::SetAnimInstanceClass(NewClass);
|
|
}
|
|
#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
void USGVirtualHandComponent::SetAnimClass(UClass* NewClass)
|
|
{
|
|
ensureAlwaysMsgf((TIsDerivedFrom<decltype(NewClass), USGVirtualHandAnimInstance>::IsDerived),
|
|
TEXT("The AnimInstance class must be a subclass of USGVirtualHandAnimInstance!"));
|
|
|
|
Super::SetAnimClass(NewClass);
|
|
}
|
|
#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
|
|
void USGVirtualHandComponent::PostInitProperties()
|
|
{
|
|
Super::PostInitProperties();
|
|
|
|
if (!Pimpl->IsUsingUserMesh())
|
|
{
|
|
Pimpl->SetDefaultMesh();
|
|
}
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void USGVirtualHandComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
|
|
|
const FName PropertyName{
|
|
PropertyChangedEvent.Property
|
|
? PropertyChangedEvent.Property->GetFName()
|
|
: NAME_None
|
|
};
|
|
|
|
static const FName RightName{
|
|
GET_MEMBER_NAME_CHECKED(USGVirtualHandComponent, bRight)
|
|
};
|
|
|
|
static const FName OverridePluginSettingsName{
|
|
FImpl::ParseVirtualHandSettingsOverridesPropertyName(
|
|
GET_MEMBER_NAME_CHECKED(FSGVirtualHandSettingsOverrides,
|
|
FSGVirtualHandSettingsOverrides::bOverridePluginSettings))
|
|
};
|
|
|
|
if (PropertyName == RightName)
|
|
{
|
|
Pimpl->RightPropertyChanged();
|
|
return;
|
|
}
|
|
|
|
if (PropertyName == OverridePluginSettingsName)
|
|
{
|
|
Pimpl->OverridePluginSettingsPropertyChanged();
|
|
return;
|
|
}
|
|
}
|
|
#endif /* WITH_EDITOR */
|
|
|
|
void USGVirtualHandComponent::InitializeComponent()
|
|
{
|
|
Super::InitializeComponent();
|
|
|
|
if (!Pimpl->IsUsingUserMesh())
|
|
{
|
|
Pimpl->SetDefaultMesh();
|
|
}
|
|
|
|
Pimpl->UpdateXRData();
|
|
}
|
|
|
|
void USGVirtualHandComponent::UninitializeComponent()
|
|
{
|
|
Super::UninitializeComponent();
|
|
|
|
const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance(GetOuter())};
|
|
USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr};
|
|
const bool bAutoStopHaptics = GetVirtualHandSettings().HapticsSettings.bAutoStopAllHapticsOnEndPlay;
|
|
if (IsValid(Glove) && Glove->IsConnected() && bAutoStopHaptics)
|
|
{
|
|
Glove->StopHaptics();
|
|
}
|
|
}
|
|
|
|
void USGVirtualHandComponent::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
Pimpl->UpdateXRData();
|
|
}
|
|
|
|
void USGVirtualHandComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
Super::EndPlay(EndPlayReason);
|
|
|
|
const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance(GetOuter())};
|
|
USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr};
|
|
const bool bAutoStopHaptics = GetVirtualHandSettings().HapticsSettings.bAutoStopAllHapticsOnEndPlay;
|
|
if (IsValid(Glove) && Glove->IsConnected() && bAutoStopHaptics)
|
|
{
|
|
Glove->StopHaptics();
|
|
}
|
|
}
|
|
|
|
void USGVirtualHandComponent::TickComponent(
|
|
const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
|
{
|
|
#if WITH_EDITOR
|
|
if (Pimpl->IsEditor())
|
|
{
|
|
EditorTick(DeltaTime, TickType, ThisTickFunction);
|
|
return;
|
|
}
|
|
#endif /* WITH_EDITOR */
|
|
|
|
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
|
|
|
Pimpl->UpdateXRData();
|
|
|
|
if (Pimpl->ShouldDrawDebugVirtualHand())
|
|
{
|
|
Pimpl->DrawDebugVirtualHand();
|
|
}
|
|
}
|
|
|
|
void USGVirtualHandComponent::SetSkeletalMesh(USkeletalMesh* NewMesh, const bool bReinitPose)
|
|
{
|
|
if (IsValid(NewMesh))
|
|
{
|
|
Super::SetSkeletalMesh(NewMesh, bReinitPose);
|
|
}
|
|
else
|
|
{
|
|
Super::SetSkeletalMesh(Pimpl->LoadDefaultMesh(), bReinitPose);
|
|
}
|
|
}
|
|
|
|
void USGVirtualHandComponent::EditorTick(
|
|
const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
|
{
|
|
Pimpl->UpdateXRData();
|
|
|
|
if (Pimpl->ShouldDrawDebugVirtualHand())
|
|
{
|
|
Pimpl->DrawDebugVirtualHand();
|
|
}
|
|
}
|
|
|
|
bool USGVirtualHandComponent::IsGloveConnected() const
|
|
{
|
|
const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance(GetOuter())};
|
|
const USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr};
|
|
return IsValid(Glove);
|
|
}
|
|
|
|
USGHapticGlove* USGVirtualHandComponent::GetConnectedGlove() const
|
|
{
|
|
const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance(GetOuter())};
|
|
USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr};
|
|
return Glove;
|
|
}
|
|
|
|
const FXRMotionControllerData& USGVirtualHandComponent::GetMotionControllerData() const
|
|
{
|
|
return Pimpl->MotionControllerData;
|
|
}
|
|
|
|
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
|
|
const FXRHandTrackingState& USGVirtualHandComponent::GetHandTrackingState() const
|
|
{
|
|
return Pimpl->HandTrackingState;
|
|
}
|
|
#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
|
|
const TArray<FName>& USGVirtualHandComponent::GetHandBoneNames() const
|
|
{
|
|
const TArray<FName>& Names{
|
|
IsRight()
|
|
? GetRightHandBoneNames()
|
|
: GetLeftHandBoneNames()
|
|
};
|
|
return Names;
|
|
}
|
|
|
|
const FName& USGVirtualHandComponent::GetHandBoneName(const int32 Joint) const
|
|
{
|
|
const FName& Name{
|
|
IsRight()
|
|
? GetRightHandBoneName(Joint)
|
|
: GetLeftHandBoneName(Joint)
|
|
};
|
|
return Name;
|
|
}
|
|
|
|
const FTransform& USGVirtualHandComponent::GetHandBoneRefTransform(const FName& BoneName) const
|
|
{
|
|
const FReferenceSkeleton& ReferenceSkeleton{Pimpl->GetRefSkeleton()};
|
|
const int32 BoneIndex = ReferenceSkeleton.FindBoneIndex(BoneName);
|
|
|
|
if (BoneIndex == INDEX_NONE)
|
|
{
|
|
SGLOG_WARNING(FString::Printf(TEXT("Could not find a bone index for the '%s' joint!"),
|
|
*BoneName.ToString()));
|
|
return FTransform::Identity;
|
|
}
|
|
|
|
const FTransform& BoneTransform{ReferenceSkeleton.GetRefBonePose()[BoneIndex]};
|
|
return BoneTransform;
|
|
}
|
|
|
|
const FTransform& USGVirtualHandComponent::GetHandRootBoneRefTransform() const
|
|
{
|
|
static constexpr int32 WristKeypointIndex = static_cast<int32>(EHandKeypoint::Wrist);
|
|
const FTransform& BoneTransform{GetHandBoneRefTransform(WristKeypointIndex)};
|
|
return BoneTransform;
|
|
}
|
|
|
|
const FTransform& USGVirtualHandComponent::GetHandBoneRefTransform(const int32 Joint) const
|
|
{
|
|
const FName& BoneName{GetHandBoneName(Joint)};
|
|
return GetHandBoneRefTransform(BoneName);
|
|
}
|
|
|
|
FRotator USGVirtualHandComponent::GetHandBoneRefRotation(const FName& BoneName) const
|
|
{
|
|
const FTransform BoneTransform{GetHandBoneRefTransform(BoneName)};
|
|
const FRotator BoneRotation{BoneTransform.GetRotation().Rotator()};
|
|
return BoneRotation;
|
|
}
|
|
|
|
FRotator USGVirtualHandComponent::GetHandBoneRefRotation(const int32 Joint) const
|
|
{
|
|
const FTransform BoneTransform{GetHandBoneRefTransform(Joint)};
|
|
const FRotator BoneRotation{BoneTransform.GetRotation().Rotator()};
|
|
return BoneRotation;
|
|
}
|
|
|
|
USGVirtualHandComponent::FImpl::FImpl(USGVirtualHandComponent* InOwner)
|
|
: bOverridePluginSettingsPropertyAlreadyModified(false),
|
|
bCachedGenerateOverlapEvents(InOwner->GetGenerateOverlapEvents()),
|
|
CachedCollisionEnabled(InOwner->GetCollisionEnabled()),
|
|
Owner(InOwner)
|
|
|
|
{
|
|
}
|
|
|
|
USGVirtualHandComponent::FImpl::~FImpl() = default;
|
|
|
|
bool USGVirtualHandComponent::FImpl::IsEditor() const
|
|
{
|
|
const UWorld* World{Owner->GetWorld()};
|
|
if (World && (World->WorldType == EWorldType::Editor || World->WorldType == EWorldType::EditorPreview))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImpl::RightPropertyChanged()
|
|
{
|
|
if (!IsUsingUserMesh())
|
|
{
|
|
SetDefaultMesh();
|
|
}
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImpl::OverridePluginSettingsPropertyChanged()
|
|
{
|
|
if (bOverridePluginSettingsPropertyAlreadyModified)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bOverridePluginSettingsPropertyAlreadyModified = true;
|
|
|
|
if (!Owner->VirtualHandSettingsOverrides.bOverridePluginSettings)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const USGSettings* Settings{USGSettings::GetInstance()};
|
|
if (!ensureAlwaysMsgf(IsValid(Settings), TEXT("The settings subsystem has not been initialized!")))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FSGVirtualHandSettings& PluginVirtualHandSettings{Settings->GetVirtualHandSettings()};
|
|
Owner->VirtualHandSettingsOverrides.bVisibleWhenHandDataUnavailable =
|
|
PluginVirtualHandSettings.bVisibleWhenHandDataUnavailable;
|
|
Owner->VirtualHandSettingsOverrides.AnimationSettings = PluginVirtualHandSettings.AnimationSettings;
|
|
Owner->VirtualHandSettingsOverrides.DebuggingSettings = PluginVirtualHandSettings.DebuggingSettings;
|
|
Owner->VirtualHandSettingsOverrides.GrabSettings = PluginVirtualHandSettings.GrabSettings;
|
|
Owner->VirtualHandSettingsOverrides.HapticsSettings = PluginVirtualHandSettings.HapticsSettings;
|
|
Owner->VirtualHandSettingsOverrides.TouchSettings = PluginVirtualHandSettings.TouchSettings;
|
|
}
|
|
|
|
FString USGVirtualHandComponent::FImpl::GetDefaultMeshPath() const
|
|
{
|
|
FString SkeletalMeshPath;
|
|
|
|
if (Owner->IsRight())
|
|
{
|
|
SkeletalMeshPath = GetDefaultRightHandMeshPath();
|
|
}
|
|
else
|
|
{
|
|
SkeletalMeshPath = GetDefaultLeftHandMeshPath();
|
|
}
|
|
|
|
return SkeletalMeshPath;
|
|
}
|
|
|
|
bool USGVirtualHandComponent::FImpl::IsUsingDefaultMesh() const
|
|
{
|
|
const USkeletalMesh* CurrentMesh{GetMesh()};
|
|
if (!IsValid(CurrentMesh))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const FString CurrentMeshPath{CurrentMesh->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 true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool USGVirtualHandComponent::FImpl::IsUsingUserMesh() const
|
|
{
|
|
const USkeletalMesh* CurrentMesh{GetMesh()};
|
|
if (!IsValid(CurrentMesh))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const FString CurrentMeshPath{CurrentMesh->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* USGVirtualHandComponent::FImpl::LoadDefaultMesh() const
|
|
{
|
|
// FIXME
|
|
// Disable this warning altogether as it results in polluting the packaging logs, at least until we ship a set of
|
|
// virtual hand meshes again.
|
|
// const FString DefaultMeshPath{GetDefaultMeshPath()};
|
|
// USkeletalMesh* DefaultMesh{
|
|
// Cast<USkeletalMesh>(StaticLoadObject(USkeletalMesh::StaticClass(), nullptr, *DefaultMeshPath))
|
|
// };
|
|
|
|
// FIXME
|
|
// Disable this warning altogether as it results in polluting the packaging logs, at least until we ship a set of
|
|
// virtual hand meshes again.
|
|
// if (!IsValid(DefaultMesh))
|
|
// {
|
|
// SGLOG_WARNING(FString::Printf(TEXT("Failed to load the default skeletal mesh: '%s'"), *DefaultMeshPath));
|
|
// }
|
|
|
|
// FIXME
|
|
//return DefaultMesh;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImpl::SetDefaultMesh() const
|
|
{
|
|
// FIXME
|
|
// Since we don't ship a default virtual hand mesh at the moment, calling SetDefaultMesh causes the following
|
|
// failed assertion due the fact that the engine won't be able to find the asset at the default location:
|
|
// Assertion failed: IsInGameThread()
|
|
// Unable to load /SenseGlove/Meshes/SK_SenseGlove_VirtualHand_*. Objects and Packages can only be loaded from the
|
|
// game thread with the currently active loader '%s'.
|
|
// One workaround would be disabling the async loading or as an alternative simply disabling this portion of the
|
|
// code should be a good-enough fix until we address this issue in its own time.
|
|
//Owner->SetSkeletalMesh(LoadDefaultMesh(), true);
|
|
// Temporary hack to make it work inside the Blueprint Editor.
|
|
#if WITH_EDITOR
|
|
// FIXME
|
|
//Owner->SetSkeletalMesh(LoadDefaultMesh(), true);
|
|
// Instead
|
|
Owner->SetSkeletalMesh(nullptr, true);
|
|
#endif /* WITH_EDITOR */
|
|
}
|
|
|
|
USkeletalMesh* USGVirtualHandComponent::FImpl::GetMesh() const
|
|
{
|
|
return Owner->GetSkeletalMeshAsset();
|
|
}
|
|
|
|
USkeleton* USGVirtualHandComponent::FImpl::GetSkeleton() const
|
|
{
|
|
USkeletalMesh* Mesh{GetMesh()};
|
|
ensureAlwaysMsgf(IsValid(Mesh), TEXT("Invalid SkeletalMesh object!"));
|
|
return Mesh->GetSkeleton();
|
|
}
|
|
|
|
const FReferenceSkeleton& USGVirtualHandComponent::FImpl::GetRefSkeleton() const
|
|
{
|
|
const USkeletalMesh* Mesh{GetMesh()};
|
|
ensureAlwaysMsgf(IsValid(Mesh), TEXT("Invalid SkeletalMesh object!"));
|
|
return Mesh->GetRefSkeleton();
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImpl::DisableCollisionResponse()
|
|
{
|
|
bCachedGenerateOverlapEvents = Owner->GetGenerateOverlapEvents();
|
|
CachedCollisionEnabled = Owner->GetCollisionEnabled();
|
|
CachedCollisionResponse = Owner->GetCollisionResponseToChannels();
|
|
Owner->SetGenerateOverlapEvents(false);
|
|
Owner->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
Owner->SetCollisionResponseToAllChannels(ECR_Ignore);
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImpl::RestoreCollisionResponse() const
|
|
{
|
|
Owner->SetGenerateOverlapEvents(bCachedGenerateOverlapEvents);
|
|
Owner->SetCollisionEnabled(CachedCollisionEnabled);
|
|
Owner->SetCollisionResponseToChannels(CachedCollisionResponse);
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImpl::UpdateXRData()
|
|
{
|
|
const EControllerHand Hand = Owner->IsRight() ? EControllerHand::Right : EControllerHand::Left;
|
|
|
|
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION < 7
|
|
{
|
|
FXRMotionControllerData Data;
|
|
Data.bValid = false;
|
|
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
|
const bool bGotMotionControllerData =
|
|
FSGXRTracker::GetMotionControllerData(Owner, Hand, Data);
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
|
|
|
(void)
|
|
bGotMotionControllerData;
|
|
MotionControllerData = MoveTemp(Data);
|
|
}
|
|
#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION < 7 */
|
|
|
|
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
|
|
{
|
|
FXRHandTrackingState State;
|
|
State.bValid = false;
|
|
const bool bGotHandTrackingState =
|
|
FSGXRTracker::GetHandTrackingState(
|
|
Owner, EXRSpaceType::UnrealWorldSpace, Hand, State);
|
|
|
|
(void) bGotHandTrackingState;
|
|
HandTrackingState = MoveTemp(State);
|
|
}
|
|
#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImpl::SetVisibility(const bool bInVisible)
|
|
{
|
|
Owner->SetHiddenInGame(!bInVisible, false);
|
|
|
|
if (bInVisible)
|
|
{
|
|
RestoreCollisionResponse();
|
|
}
|
|
else
|
|
{
|
|
DisableCollisionResponse();
|
|
}
|
|
|
|
TriggerHandVisibilityChangedEvent(bInVisible);
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImpl::UpdateVisibility()
|
|
{
|
|
const bool bHandDataAvailable =
|
|
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
|
|
HandTrackingState.bValid
|
|
#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
MotionControllerData.bValid
|
|
&& MotionControllerData.DeviceVisualType == EXRVisualType::Hand
|
|
#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
;
|
|
const bool bHandVisible = Owner->IsVisible();
|
|
const bool bShouldBeVisibleWhenHandDataUnavailable =
|
|
Owner->GetVirtualHandSettings().bVisibleWhenHandDataUnavailable;
|
|
|
|
bool bSetNewVisibility = false;
|
|
|
|
if (bHandDataAvailable)
|
|
{
|
|
if (bHandVisible)
|
|
{
|
|
bSetNewVisibility = false;
|
|
}
|
|
else
|
|
{
|
|
bSetNewVisibility = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bHandVisible)
|
|
{
|
|
if (bShouldBeVisibleWhenHandDataUnavailable)
|
|
{
|
|
bSetNewVisibility = false;
|
|
}
|
|
else
|
|
{
|
|
bSetNewVisibility = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bShouldBeVisibleWhenHandDataUnavailable)
|
|
{
|
|
bSetNewVisibility = true;
|
|
}
|
|
else
|
|
{
|
|
bSetNewVisibility = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bSetNewVisibility)
|
|
{
|
|
SetVisibility(bHandDataAvailable);
|
|
}
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImpl::DrawDebugVirtualHand() const
|
|
{
|
|
const UWorld* World{Owner->GetWorld()};
|
|
|
|
if (!IsValid(Owner->WristTracker))
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
|
|
if (!HandTrackingState.bValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FSGDebugVirtualHand::Draw(World, HandTrackingState, Owner->GetVirtualHandSettings().DebuggingSettings);
|
|
#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
|
|
if (!MotionControllerData.bValid
|
|
|| MotionControllerData.DeviceVisualType == EXRVisualType::Hand)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FSGDebugVirtualHand::Draw(World, MotionControllerData, Owner->GetVirtualHandSettings().DebuggingSettings);
|
|
#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImpl::TriggerHandVisibilityChangedEvent(const bool bNewVisibility) const
|
|
{
|
|
Owner->HandVisibilityChangedEvent.Broadcast(bNewVisibility);
|
|
Owner->OnHandVisibilityChanged(bNewVisibility);
|
|
}
|
|
|
|
void USGVirtualHandComponent::FImplDeleter::operator()(const FImpl* P) const
|
|
{
|
|
delete P;
|
|
} |