Files
Plugin/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp
T

896 lines
28 KiB
C++

/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @section LICENSE
*
* (The MIT License)
*
* Copyright (c) 2020 - 2025 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 "SGBuildHacks/SGPlatform.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;
}
#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;
}