Config for building for Quest
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "LiveLinkOculusXRMovementSourceFactory.h"
|
||||
#include "IOculusXRMovementModule.h"
|
||||
|
||||
#include "Features/IModularFeatures.h"
|
||||
#include "ILiveLinkClient.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMovement"
|
||||
|
||||
FText ULiveLinkOculusXRMovementSourceFactory::GetSourceDisplayName() const
|
||||
{
|
||||
return LOCTEXT("OculusXRMovementLiveLinkSourceName", "Meta MovementSDK Live Link");
|
||||
}
|
||||
|
||||
FText ULiveLinkOculusXRMovementSourceFactory::GetSourceTooltip() const
|
||||
{
|
||||
return LOCTEXT("OculusXRMovementLiveLinkSourceTooltip", "Meta MovementSDK Live Link Source");
|
||||
}
|
||||
|
||||
ULiveLinkOculusXRMovementSourceFactory::EMenuType ULiveLinkOculusXRMovementSourceFactory::GetMenuType() const
|
||||
{
|
||||
if (IModularFeatures::Get().IsModularFeatureAvailable(ILiveLinkClient::ModularFeatureName))
|
||||
{
|
||||
const ILiveLinkClient& LiveLinkClient = IModularFeatures::Get().GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName);
|
||||
|
||||
if (!IOculusXRMovementModule::Get().IsLiveLinkSourceValid() || !LiveLinkClient.HasSourceBeenAdded(IOculusXRMovementModule::Get().GetLiveLinkSource()))
|
||||
{
|
||||
return EMenuType::MenuEntry;
|
||||
}
|
||||
}
|
||||
return EMenuType::Disabled;
|
||||
}
|
||||
|
||||
TSharedPtr<ILiveLinkSource> ULiveLinkOculusXRMovementSourceFactory::CreateSource(const FString& ConnectionString) const
|
||||
{
|
||||
return IOculusXRMovementModule::Get().GetLiveLinkSource();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LiveLinkSourceFactory.h"
|
||||
#include "LiveLinkOculusXRMovementSourceFactory.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ULiveLinkOculusXRMovementSourceFactory : public ULiveLinkSourceFactory
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
|
||||
virtual FText GetSourceDisplayName() const override;
|
||||
virtual FText GetSourceTooltip() const override;
|
||||
|
||||
virtual EMenuType GetMenuType() const override;
|
||||
virtual TSharedPtr<ILiveLinkSource> CreateSource(const FString& ConnectionString) const override;
|
||||
|
||||
TSharedPtr<class SLiveLinkOculusXRMovementSourceEditor> ActiveSourceEditor;
|
||||
};
|
||||
@@ -0,0 +1,263 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRBodyTrackingComponent.h"
|
||||
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "OculusXRPluginWrapper.h"
|
||||
#include "OculusXRMovementFunctionLibrary.h"
|
||||
#include "OculusXRMovementLog.h"
|
||||
#include "OculusXRTelemetryMovementEvents.h"
|
||||
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
static TAutoConsoleVariable<int32> CVarOVRBodyDebugDraw(
|
||||
TEXT("ovr.BodyDebugDraw"),
|
||||
0,
|
||||
TEXT("Enables or disables debug drawing for body tracking.\n")
|
||||
TEXT("<=0: disabled (no drawing)\n")
|
||||
TEXT(" 1: enabled (debug drawing)\n"));
|
||||
#endif
|
||||
|
||||
int UOculusXRBodyTrackingComponent::TrackingInstanceCount = 0;
|
||||
|
||||
UOculusXRBodyTrackingComponent::UOculusXRBodyTrackingComponent()
|
||||
: BodyTrackingMode(EOculusXRBodyTrackingMode::PositionAndRotation)
|
||||
, ConfidenceThreshold(0.f)
|
||||
, WorldToMeters(100.f)
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
PrimaryComponentTick.bStartWithTickEnabled = true;
|
||||
|
||||
// Setup defaults
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRoot, "Root");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyHips, "Hips");
|
||||
BoneNames.Add(EOculusXRBoneID::BodySpineLower, "SpineLower");
|
||||
BoneNames.Add(EOculusXRBoneID::BodySpineMiddle, "SpineMiddle");
|
||||
BoneNames.Add(EOculusXRBoneID::BodySpineUpper, "SpineUpper");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyChest, "Chest");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyNeck, "Neck");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyHead, "Head");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftShoulder, "LeftShoulder");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftScapula, "LeftScapula");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftArmUpper, "LeftArmUpper");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftArmLower, "LeftArmLower");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandWristTwist, "LeftHandWristTwist");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightShoulder, "RightShoulder");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightScapula, "RightScapula");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightArmUpper, "RightArmUpper");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightArmLower, "RightArmLower");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandWristTwist, "RightHandWristTwist");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandPalm, "LeftHandPalm");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandWrist, "LeftHandWrist");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandThumbMetacarpal, "LeftHandThumbMetacarpal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandThumbProximal, "LeftHandThumbProximal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandThumbDistal, "LeftHandThumbDistal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandThumbTip, "LeftHandThumbTip");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandIndexMetacarpal, "LeftHandIndexMetacarpal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandIndexProximal, "LeftHandIndexProximal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandIndexIntermediate, "LeftHandIndexIntermediate");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandIndexDistal, "LeftHandIndexDistal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandIndexTip, "LeftHandIndexTip");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandMiddleMetacarpal, "LeftHandMiddleMetacarpal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandMiddleProximal, "LeftHandMiddleProximal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandMiddleIntermediate, "LeftHandMiddleIntermediate");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandMiddleDistal, "LeftHandMiddleDistal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandMiddleTip, "LeftHandMiddleTip");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandRingMetacarpal, "LeftHandRingMetacarpal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandRingProximal, "LeftHandRingProximal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandRingIntermediate, "LeftHandRingIntermediate");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandRingDistal, "LeftHandRingDistal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandRingTip, "LeftHandRingTip");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandLittleMetacarpal, "LeftHandLittleMetacarpal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandLittleProximal, "LeftHandLittleProximal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandLittleIntermediate, "LeftHandLittleIntermediate");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandLittleDistal, "LeftHandLittleDistal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftHandLittleTip, "LeftHandLittleTip");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandPalm, "RightHandPalm");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandWrist, "RightHandWrist");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandThumbMetacarpal, "RightHandThumbMetacarpal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandThumbProximal, "RightHandThumbProximal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandThumbDistal, "RightHandThumbDistal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandThumbTip, "RightHandThumbTip");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandIndexMetacarpal, "RightHandIndexMetacarpal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandIndexProximal, "RightHandIndexProximal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandIndexIntermediate, "RightHandIndexIntermediate");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandIndexDistal, "RightHandIndexDistal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandIndexTip, "RightHandIndexTip");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandMiddleMetacarpal, "RightHandMiddleMetacarpal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandMiddleProximal, "RightHandMiddleProximal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandMiddleIntermediate, "RightHandMiddleIntermediate");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandMiddleDistal, "RightHandMiddleDistal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandMiddleTip, "RightHandMiddleTip");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandRingMetacarpal, "RightHandRingMetacarpal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandRingProximal, "RightHandRingProximal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandRingIntermediate, "RightHandRingIntermediate");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandRingDistal, "RightHandRingDistal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandRingTip, "RightHandRingTip");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandLittleMetacarpal, "RightHandLittleMetacarpal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandLittleProximal, "RightHandLittleProximal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandLittleIntermediate, "RightHandLittleIntermediate");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandLittleDistal, "RightHandLittleDistal");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightHandLittleTip, "RightHandLittleTip");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftUpperLeg, "LeftUpperLeg");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftLowerLeg, "LeftLowerLeg");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftFootAnkleTwist, "LeftFootAnkleTwist");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftFootAnkle, "LeftFootAnkle");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftFootSubtalar, "LeftFootSubtalar");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftFootTransverse, "LeftFootTransverse");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyLeftFootBall, "LeftFootBall");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightUpperLeg, "RightUpperLeg");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightLowerLeg, "RightLowerLeg");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightFootAnkleTwist, "RightFootAnkleTwist");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightFootAnkle, "RightFootAnkle");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightFootSubtalar, "RightFootSubtalar");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightFootTransverse, "RightFootTransverse");
|
||||
BoneNames.Add(EOculusXRBoneID::BodyRightFootBall, "RightFootBall");
|
||||
|
||||
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FMovementSDKBodyStart>(static_cast<int>(GetTypeHash(this)));
|
||||
}
|
||||
|
||||
void UOculusXRBodyTrackingComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (!UOculusXRMovementFunctionLibrary::IsBodyTrackingSupported())
|
||||
{
|
||||
// Early exit if body tracking isn't supported
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Body tracking is not supported. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!OculusXRHMD::GetUnitScaleFactorFromSettings(GetWorld(), WorldToMeters))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Cannot get world settings. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
}
|
||||
|
||||
if (!InitializeBodyBones())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Failed to initialize body data. (%s: %s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UOculusXRMovementFunctionLibrary::StartBodyTracking())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Failed to start body tracking. (%s: %s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
return;
|
||||
}
|
||||
++TrackingInstanceCount;
|
||||
}
|
||||
|
||||
void UOculusXRBodyTrackingComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (IsComponentTickEnabled())
|
||||
{
|
||||
if (--TrackingInstanceCount == 0)
|
||||
{
|
||||
if (!UOculusXRMovementFunctionLibrary::StopBodyTracking())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Failed to stop body tracking. (%s: %s)"), *GetOwner()->GetName(), *GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
void UOculusXRBodyTrackingComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
if (UOculusXRMovementFunctionLibrary::TryGetBodyState(BodyState, WorldToMeters))
|
||||
{
|
||||
if (BodyState.IsActive && BodyState.Confidence > ConfidenceThreshold)
|
||||
{
|
||||
for (int i = 0; i < BodyState.Joints.Num(); ++i)
|
||||
{
|
||||
const FOculusXRBodyJoint& Joint = BodyState.Joints[i];
|
||||
if (!Joint.bIsValid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FVector& Position = Joint.Position;
|
||||
const FRotator& Orientation = Joint.Orientation;
|
||||
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
if (CVarOVRBodyDebugDraw.GetValueOnGameThread() > 0)
|
||||
{
|
||||
const FTransform& ParentTransform = GetOwner()->GetActorTransform();
|
||||
|
||||
FVector DebugPosition = ParentTransform.TransformPosition(Position);
|
||||
FRotator DebugOrientation = ParentTransform.TransformRotation(Orientation.Quaternion()).Rotator();
|
||||
|
||||
DrawDebugLine(GetWorld(), DebugPosition, DebugPosition + DebugOrientation.Quaternion().GetUpVector(), FColor::Blue);
|
||||
DrawDebugLine(GetWorld(), DebugPosition, DebugPosition + DebugOrientation.Quaternion().GetForwardVector(), FColor::Red);
|
||||
DrawDebugLine(GetWorld(), DebugPosition, DebugPosition + DebugOrientation.Quaternion().GetRightVector(), FColor::Green);
|
||||
}
|
||||
#endif
|
||||
|
||||
int32* BoneIndex = MappedBoneIndices.Find(static_cast<EOculusXRBoneID>(i));
|
||||
if (BoneIndex != nullptr)
|
||||
{
|
||||
switch (BodyTrackingMode)
|
||||
{
|
||||
case EOculusXRBodyTrackingMode::PositionAndRotation:
|
||||
SetBoneTransformByName(BoneNames[static_cast<EOculusXRBoneID>(i)], FTransform(Orientation, Position), EBoneSpaces::ComponentSpace);
|
||||
break;
|
||||
case EOculusXRBodyTrackingMode::RotationOnly:
|
||||
SetBoneRotationByName(BoneNames[static_cast<EOculusXRBoneID>(i)], Orientation, EBoneSpaces::ComponentSpace);
|
||||
break;
|
||||
case EOculusXRBodyTrackingMode::NoTracking:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Verbose, TEXT("Failed to get body state (%s:%s)."), *GetOwner()->GetName(), *GetName());
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRBodyTrackingComponent::ResetAllBoneTransforms()
|
||||
{
|
||||
for (int i = 0; i < BodyState.Joints.Num(); ++i)
|
||||
{
|
||||
int32* BoneIndex = MappedBoneIndices.Find(static_cast<EOculusXRBoneID>(i));
|
||||
if (BoneIndex != nullptr)
|
||||
{
|
||||
ResetBoneTransformByName(BoneNames[static_cast<EOculusXRBoneID>(i)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UOculusXRBodyTrackingComponent::InitializeBodyBones()
|
||||
{
|
||||
USkeletalMesh* BodyMesh = Cast<USkeletalMesh>(GetSkinnedAsset());
|
||||
if (BodyMesh == nullptr)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Display, TEXT("No SkeletalMesh in this component."));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& it : BoneNames)
|
||||
{
|
||||
int32 BoneIndex = GetBoneIndex(it.Value);
|
||||
|
||||
if (BoneIndex == INDEX_NONE)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Display, TEXT("Could not find bone %s in skeletal mesh %s"), *StaticEnum<EOculusXRBoneID>()->GetValueAsString(it.Key), *BodyMesh->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
MappedBoneIndices.Add(it.Key, BoneIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXREyeTrackingComponent.h"
|
||||
|
||||
#include "GameFramework/WorldSettings.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "OculusXRHMDPrivate.h"
|
||||
#include "OculusXRPluginWrapper.h"
|
||||
#include "OculusXRMovementFunctionLibrary.h"
|
||||
#include "OculusXRMovementHelpers.h"
|
||||
#include "OculusXRMovementLog.h"
|
||||
#include "OculusXRTelemetryMovementEvents.h"
|
||||
|
||||
int UOculusXREyeTrackingComponent::TrackingInstanceCount = 0;
|
||||
|
||||
UOculusXREyeTrackingComponent::UOculusXREyeTrackingComponent()
|
||||
: TargetMeshComponentName(NAME_None)
|
||||
, bUpdatePosition(true)
|
||||
, bUpdateRotation(true)
|
||||
, ConfidenceThreshold(0.f)
|
||||
, bAcceptInvalid(false)
|
||||
, WorldToMeters(100.f)
|
||||
, TargetPoseableMeshComponent(nullptr)
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
PrimaryComponentTick.bStartWithTickEnabled = true;
|
||||
|
||||
EyeToBone.Add(EOculusXREye::Left, "LeftEye");
|
||||
EyeToBone.Add(EOculusXREye::Right, "RightEye");
|
||||
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FMovementSDKEyeStart>(static_cast<int>(GetTypeHash(this)));
|
||||
}
|
||||
|
||||
void UOculusXREyeTrackingComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (!UOculusXRMovementFunctionLibrary::IsEyeTrackingSupported())
|
||||
{
|
||||
// Early exit if eye tracking isn't supported
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Eye tracking is not supported. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try & check initializing the eye data
|
||||
if (!InitializeEyes())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Failed to initialize eye tracking data. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
}
|
||||
|
||||
if (!UOculusXRMovementFunctionLibrary::StartEyeTracking())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Failed to start eye tracking. (%s: %s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
return;
|
||||
}
|
||||
++TrackingInstanceCount;
|
||||
}
|
||||
|
||||
void UOculusXREyeTrackingComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (IsComponentTickEnabled())
|
||||
{
|
||||
if (--TrackingInstanceCount == 0)
|
||||
{
|
||||
if (!UOculusXRMovementFunctionLibrary::StopEyeTracking())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Failed to stop eye tracking. (%s: %s)"), *GetOwner()->GetName(), *GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
void UOculusXREyeTrackingComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
if (!IsValid(TargetPoseableMeshComponent))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, VeryVerbose, TEXT("No target mesh specified. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
FOculusXREyeGazesState EyeGazesState;
|
||||
|
||||
if (UOculusXRMovementFunctionLibrary::TryGetEyeGazesState(EyeGazesState, WorldToMeters))
|
||||
{
|
||||
for (uint8 i = 0u; i < static_cast<uint8>(EOculusXREye::COUNT); ++i)
|
||||
{
|
||||
if (PerEyeData[i].EyeIsMapped)
|
||||
{
|
||||
const auto& Bone = PerEyeData[i].MappedBoneName;
|
||||
const auto& EyeGaze = EyeGazesState.EyeGazes[i];
|
||||
if ((bAcceptInvalid || EyeGaze.bIsValid) && (EyeGaze.Confidence >= ConfidenceThreshold))
|
||||
{
|
||||
int32 BoneIndex = TargetPoseableMeshComponent->GetBoneIndex(Bone);
|
||||
FTransform CurrentTransform = TargetPoseableMeshComponent->GetBoneTransformByName(Bone, EBoneSpaces::ComponentSpace);
|
||||
|
||||
if (bUpdatePosition)
|
||||
{
|
||||
CurrentTransform.SetLocation(EyeGaze.Position);
|
||||
}
|
||||
|
||||
if (bUpdateRotation)
|
||||
{
|
||||
CurrentTransform.SetRotation(EyeGaze.Orientation.Quaternion() * PerEyeData[i].InitialRotation);
|
||||
}
|
||||
|
||||
TargetPoseableMeshComponent->SetBoneTransformByName(Bone, CurrentTransform, EBoneSpaces::ComponentSpace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, VeryVerbose, TEXT("Failed to get Eye state from EyeTrackingComponent. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXREyeTrackingComponent::ClearRotationValues()
|
||||
{
|
||||
if (!IsValid(TargetPoseableMeshComponent))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, VeryVerbose, TEXT("No target mesh specified. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8 i = 0u; i < static_cast<uint8>(EOculusXREye::COUNT); ++i)
|
||||
{
|
||||
if (PerEyeData[i].EyeIsMapped)
|
||||
{
|
||||
const auto& Bone = PerEyeData[i].MappedBoneName;
|
||||
|
||||
int32 BoneIndex = TargetPoseableMeshComponent->GetBoneIndex(Bone);
|
||||
FTransform CurrentTransform = TargetPoseableMeshComponent->GetBoneTransformByName(Bone, EBoneSpaces::ComponentSpace);
|
||||
|
||||
CurrentTransform.SetRotation(PerEyeData[i].InitialRotation);
|
||||
|
||||
TargetPoseableMeshComponent->SetBoneTransformByName(Bone, CurrentTransform, EBoneSpaces::ComponentSpace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UOculusXREyeTrackingComponent::InitializeEyes()
|
||||
{
|
||||
bool bIsAnythingMapped = false;
|
||||
|
||||
TargetPoseableMeshComponent = OculusXRUtility::FindComponentByName<UPoseableMeshComponent>(GetOwner(), TargetMeshComponentName);
|
||||
|
||||
if (!IsValid(TargetPoseableMeshComponent))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Could not find mesh with name (%s) for component. (%s:%s)"), *TargetMeshComponentName.ToString(), *GetOwner()->GetName(), *GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8 i = 0u; i < static_cast<uint8>(EOculusXREye::COUNT); ++i)
|
||||
{
|
||||
const EOculusXREye Eye = static_cast<EOculusXREye>(i);
|
||||
const FName* BoneNameForThisEye = EyeToBone.Find(Eye);
|
||||
PerEyeData[i].EyeIsMapped = (nullptr != BoneNameForThisEye);
|
||||
|
||||
if (PerEyeData[i].EyeIsMapped)
|
||||
{
|
||||
int32 BoneIndex = TargetPoseableMeshComponent->GetBoneIndex(*BoneNameForThisEye);
|
||||
if (BoneIndex == INDEX_NONE)
|
||||
{
|
||||
PerEyeData[i].EyeIsMapped = false; // Eye is explicitly mapped to a bone. But the bone name doesn't exist.
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Could not find bone by name (%s) in mesh %s. (%s:%s)"), *BoneNameForThisEye->ToString(), *TargetPoseableMeshComponent->GetName(), *GetOwner()->GetName(), *GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
PerEyeData[i].MappedBoneName = *BoneNameForThisEye;
|
||||
PerEyeData[i].InitialRotation = TargetPoseableMeshComponent->GetBoneTransformByName(*BoneNameForThisEye, EBoneSpaces::ComponentSpace).GetRotation();
|
||||
bIsAnythingMapped = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Display, TEXT("Eye (%s) is not mapped to any bone on mesh (%s)"), *StaticEnum<EOculusXREye>()->GetValueAsString(Eye), *TargetPoseableMeshComponent->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
if (!bIsAnythingMapped)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Component name -- %s:%s, doesn't have a valid configuration."), *GetOwner()->GetName(), *GetName());
|
||||
}
|
||||
|
||||
if (!OculusXRHMD::GetUnitScaleFactorFromSettings(GetWorld(), WorldToMeters))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Cannot get world settings. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
}
|
||||
|
||||
return bIsAnythingMapped;
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRFaceTrackingComponent.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "OculusXRPluginWrapper.h"
|
||||
#include "OculusXRMovementFunctionLibrary.h"
|
||||
#include "OculusXRMovementHelpers.h"
|
||||
#include "OculusXRMovementLog.h"
|
||||
#include "OculusXRTelemetryMovementEvents.h"
|
||||
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Math/UnrealMathUtility.h"
|
||||
|
||||
int UOculusXRFaceTrackingComponent::TrackingInstanceCount = 0;
|
||||
|
||||
UOculusXRFaceTrackingComponent::UOculusXRFaceTrackingComponent()
|
||||
: TargetMeshComponentName(NAME_None)
|
||||
, InvalidFaceDataResetTime(2.0f)
|
||||
, bUpdateFace(true)
|
||||
, TargetMeshComponent(nullptr)
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
PrimaryComponentTick.bStartWithTickEnabled = true;
|
||||
|
||||
// Some defaults
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::BrowLowererL, "browLowerer_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::BrowLowererR, "browLowerer_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::CheekPuffL, "cheekPuff_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::CheekPuffR, "cheekPuff_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::CheekRaiserL, "cheekRaiser_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::CheekRaiserR, "cheekRaiser_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::CheekSuckL, "cheekSuck_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::CheekSuckR, "cheekSuck_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::ChinRaiserB, "chinRaiser_B");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::ChinRaiserT, "chinRaiser_T");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::DimplerL, "dimpler_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::DimplerR, "dimpler_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::EyesClosedL, "eyesClosed_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::EyesClosedR, "eyesClosed_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::EyesLookDownL, "eyesLookDown_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::EyesLookDownR, "eyesLookDown_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::EyesLookLeftL, "eyesLookLeft_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::EyesLookLeftR, "eyesLookLeft_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::EyesLookRightL, "eyesLookRight_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::EyesLookRightR, "eyesLookRight_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::EyesLookUpL, "eyesLookUp_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::EyesLookUpR, "eyesLookUp_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::InnerBrowRaiserL, "innerBrowRaiser_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::InnerBrowRaiserR, "innerBrowRaiser_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::JawDrop, "jawDrop");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::JawSidewaysLeft, "jawSidewaysLeft");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::JawSidewaysRight, "jawSidewaysRight");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::JawThrust, "jawThrust");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LidTightenerL, "lidTightener_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LidTightenerR, "lidTightener_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipCornerDepressorL, "lipCornerDepressor_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipCornerDepressorR, "lipCornerDepressor_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipCornerPullerL, "lipCornerPuller_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipCornerPullerR, "lipCornerPuller_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipFunnelerLB, "lipFunneler_LB");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipFunnelerLT, "lipFunneler_LT");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipFunnelerRB, "lipFunneler_RB");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipFunnelerRT, "lipFunneler_RT");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipPressorL, "lipPressor_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipPressorR, "lipPressor_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipPuckerL, "lipPucker_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipPuckerR, "lipPucker_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipStretcherL, "lipStretcher_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipStretcherR, "lipStretcher_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipSuckLB, "lipSuck_LB");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipSuckLT, "lipSuck_LT");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipSuckRB, "lipSuck_RB");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipSuckRT, "lipSuck_RT");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipTightenerL, "lipTightener_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipTightenerR, "lipTightener_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LipsToward, "lipsToward");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LowerLipDepressorL, "lowerLipDepressor_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::LowerLipDepressorR, "lowerLipDepressor_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::MouthLeft, "mouthLeft");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::MouthRight, "mouthRight");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::NoseWrinklerL, "noseWrinkler_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::NoseWrinklerR, "noseWrinkler_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::OuterBrowRaiserL, "outerBrowRaiser_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::OuterBrowRaiserR, "outerBrowRaiser_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::UpperLidRaiserL, "upperLidRaiser_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::UpperLidRaiserR, "upperLidRaiser_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::UpperLipRaiserL, "upperLipRaiser_L");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::UpperLipRaiserR, "upperLipRaiser_R");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::TongueTipInterdental, "tongueTipInterdental");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::TongueTipAlveolar, "tongueTipAlveolar");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::TongueFrontDorsalPalate, "tongueFrontDorsalPalate");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::TongueMidDorsalPalate, "tongueMidDorsalPalate");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::TongueBackDorsalVelar, "tongueBackDorsalVelar");
|
||||
ExpressionNames.Add(EOculusXRFaceExpression::TongueOut, "tongueOut");
|
||||
|
||||
const int defaultFaceExpressionModifierLength = 33;
|
||||
ExpressionModifiers.SetNum(defaultFaceExpressionModifierLength);
|
||||
ExpressionModifiers[0].FaceExpressions = { EOculusXRFaceExpression::EyesClosedL, EOculusXRFaceExpression::EyesClosedR };
|
||||
ExpressionModifiers[1].FaceExpressions = { EOculusXRFaceExpression::EyesLookDownL, EOculusXRFaceExpression::EyesLookDownR };
|
||||
ExpressionModifiers[2].FaceExpressions = { EOculusXRFaceExpression::EyesLookLeftL, EOculusXRFaceExpression::EyesLookLeftR };
|
||||
ExpressionModifiers[3].FaceExpressions = { EOculusXRFaceExpression::EyesLookRightL, EOculusXRFaceExpression::EyesLookRightR };
|
||||
ExpressionModifiers[4].FaceExpressions = { EOculusXRFaceExpression::EyesLookUpL, EOculusXRFaceExpression::EyesLookUpR };
|
||||
ExpressionModifiers[5].FaceExpressions = { EOculusXRFaceExpression::LidTightenerL, EOculusXRFaceExpression::LidTightenerR };
|
||||
ExpressionModifiers[6].FaceExpressions = { EOculusXRFaceExpression::UpperLidRaiserL, EOculusXRFaceExpression::UpperLidRaiserR };
|
||||
ExpressionModifiers[7].FaceExpressions = { EOculusXRFaceExpression::JawDrop };
|
||||
ExpressionModifiers[8].FaceExpressions = { EOculusXRFaceExpression::JawSidewaysLeft, EOculusXRFaceExpression::JawSidewaysRight };
|
||||
ExpressionModifiers[9].FaceExpressions = { EOculusXRFaceExpression::JawThrust };
|
||||
ExpressionModifiers[10].FaceExpressions = { EOculusXRFaceExpression::LipFunnelerLB, EOculusXRFaceExpression::LipFunnelerLT };
|
||||
ExpressionModifiers[11].FaceExpressions = { EOculusXRFaceExpression::LipFunnelerRB, EOculusXRFaceExpression::LipFunnelerRT };
|
||||
ExpressionModifiers[12].FaceExpressions = { EOculusXRFaceExpression::LipPuckerL, EOculusXRFaceExpression::LipPuckerR };
|
||||
ExpressionModifiers[13].FaceExpressions = { EOculusXRFaceExpression::LipSuckLB, EOculusXRFaceExpression::LipSuckLT };
|
||||
ExpressionModifiers[14].FaceExpressions = { EOculusXRFaceExpression::LipSuckRB, EOculusXRFaceExpression::LipSuckRT };
|
||||
ExpressionModifiers[15].FaceExpressions = { EOculusXRFaceExpression::LipsToward };
|
||||
ExpressionModifiers[16].FaceExpressions = { EOculusXRFaceExpression::LowerLipDepressorL, EOculusXRFaceExpression::LowerLipDepressorR };
|
||||
ExpressionModifiers[17].FaceExpressions = { EOculusXRFaceExpression::ChinRaiserB, EOculusXRFaceExpression::ChinRaiserT };
|
||||
ExpressionModifiers[18].FaceExpressions = { EOculusXRFaceExpression::LipCornerDepressorL, EOculusXRFaceExpression::LipCornerDepressorR };
|
||||
ExpressionModifiers[19].FaceExpressions = { EOculusXRFaceExpression::LipCornerPullerL, EOculusXRFaceExpression::LipCornerPullerR };
|
||||
ExpressionModifiers[20].FaceExpressions = { EOculusXRFaceExpression::LipStretcherL, EOculusXRFaceExpression::LipStretcherR };
|
||||
ExpressionModifiers[21].FaceExpressions = { EOculusXRFaceExpression::MouthLeft, EOculusXRFaceExpression::MouthRight };
|
||||
ExpressionModifiers[22].FaceExpressions = { EOculusXRFaceExpression::LipPressorL, EOculusXRFaceExpression::LipPressorR };
|
||||
ExpressionModifiers[23].FaceExpressions = { EOculusXRFaceExpression::LipTightenerL, EOculusXRFaceExpression::LipTightenerR };
|
||||
ExpressionModifiers[24].FaceExpressions = { EOculusXRFaceExpression::UpperLipRaiserL, EOculusXRFaceExpression::UpperLipRaiserR };
|
||||
ExpressionModifiers[25].FaceExpressions = { EOculusXRFaceExpression::CheekPuffL, EOculusXRFaceExpression::CheekPuffR };
|
||||
ExpressionModifiers[26].FaceExpressions = { EOculusXRFaceExpression::CheekRaiserL, EOculusXRFaceExpression::CheekRaiserR };
|
||||
ExpressionModifiers[27].FaceExpressions = { EOculusXRFaceExpression::CheekSuckL, EOculusXRFaceExpression::CheekSuckR };
|
||||
ExpressionModifiers[28].FaceExpressions = { EOculusXRFaceExpression::DimplerL, EOculusXRFaceExpression::DimplerR };
|
||||
ExpressionModifiers[29].FaceExpressions = { EOculusXRFaceExpression::NoseWrinklerL, EOculusXRFaceExpression::NoseWrinklerR };
|
||||
ExpressionModifiers[30].FaceExpressions = { EOculusXRFaceExpression::BrowLowererL, EOculusXRFaceExpression::BrowLowererR };
|
||||
ExpressionModifiers[31].FaceExpressions = { EOculusXRFaceExpression::InnerBrowRaiserL, EOculusXRFaceExpression::InnerBrowRaiserR };
|
||||
ExpressionModifiers[32].FaceExpressions = { EOculusXRFaceExpression::OuterBrowRaiserL, EOculusXRFaceExpression::OuterBrowRaiserR };
|
||||
|
||||
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FMovementSDKFaceStart>(static_cast<int>(GetTypeHash(this)));
|
||||
}
|
||||
|
||||
void UOculusXRFaceTrackingComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (!UOculusXRMovementFunctionLibrary::IsFaceTrackingSupported())
|
||||
{
|
||||
// Early exit if face tracking isn't supported
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Face tracking is not supported. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TargetMeshComponentName == NAME_None)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Invalid mesh component name. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InitializeFaceTracking())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Failed to initialize face tracking. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UOculusXRMovementFunctionLibrary::StartFaceTracking())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Failed to start face tracking. (%s: %s)"), *GetOwner()->GetName(), *GetName());
|
||||
SetComponentTickEnabled(false);
|
||||
return;
|
||||
}
|
||||
++TrackingInstanceCount;
|
||||
}
|
||||
|
||||
void UOculusXRFaceTrackingComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (IsComponentTickEnabled())
|
||||
{
|
||||
if (--TrackingInstanceCount == 0)
|
||||
{
|
||||
if (!UOculusXRMovementFunctionLibrary::StopFaceTracking())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Failed to stop face tracking. (%s: %s)"), *GetOwner()->GetName(), *GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
void UOculusXRFaceTrackingComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
if (!IsValid(TargetMeshComponent))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, VeryVerbose, TEXT("No target mesh specified. (%s:%s)"), *GetOwner()->GetName(), *GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (UOculusXRMovementFunctionLibrary::TryGetFaceState(FaceState) && bUpdateFace)
|
||||
{
|
||||
InvalidFaceStateTimer = 0.0f;
|
||||
|
||||
MorphTargets.ResetMorphTargetCurves(TargetMeshComponent);
|
||||
|
||||
for (int32 FaceExpressionIndex = 0; FaceExpressionIndex < static_cast<int32>(EOculusXRFaceExpression::COUNT); ++FaceExpressionIndex)
|
||||
{
|
||||
if (ExpressionValid[FaceExpressionIndex])
|
||||
{
|
||||
FName ExpressionName = ExpressionNames[static_cast<EOculusXRFaceExpression>(FaceExpressionIndex)];
|
||||
MorphTargets.SetMorphTarget(ExpressionName, FaceState.ExpressionWeights[FaceExpressionIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
if (bUseModifiers)
|
||||
{
|
||||
for (int32 FaceExpressionModifierIndex = 0; FaceExpressionModifierIndex < ExpressionModifiers.Num(); ++FaceExpressionModifierIndex)
|
||||
{
|
||||
for (int32 FaceExpressionIndex = 0; FaceExpressionIndex < ExpressionModifiers[FaceExpressionModifierIndex].FaceExpressions.Num(); ++FaceExpressionIndex)
|
||||
{
|
||||
auto Expression = ExpressionModifiers[FaceExpressionModifierIndex].FaceExpressions[FaceExpressionIndex];
|
||||
if (ExpressionValid[static_cast<int32>(Expression)])
|
||||
{
|
||||
FName ExpressionName = ExpressionNames[Expression];
|
||||
float currentValue = MorphTargets.GetMorphTarget(ExpressionName);
|
||||
|
||||
currentValue = FMath::Clamp(
|
||||
currentValue * ExpressionModifiers[FaceExpressionModifierIndex].Multiplier,
|
||||
ExpressionModifiers[FaceExpressionModifierIndex].MinValue,
|
||||
ExpressionModifiers[FaceExpressionModifierIndex].MaxValue);
|
||||
|
||||
MorphTargets.SetMorphTarget(ExpressionName, currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InvalidFaceStateTimer += DeltaTime;
|
||||
if (InvalidFaceStateTimer >= InvalidFaceDataResetTime)
|
||||
{
|
||||
MorphTargets.ResetMorphTargetCurves(TargetMeshComponent);
|
||||
}
|
||||
}
|
||||
|
||||
MorphTargets.ApplyMorphTargets(TargetMeshComponent);
|
||||
}
|
||||
|
||||
void UOculusXRFaceTrackingComponent::SetExpressionValue(EOculusXRFaceExpression Expression, float Value)
|
||||
{
|
||||
if (Expression >= EOculusXRFaceExpression::COUNT)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Cannot set expression value with invalid expression index."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ExpressionValid[static_cast<int32>(Expression)])
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Cannot set expression value for an expression with an invalid associated morph target name. Expression name: %s"), *StaticEnum<EOculusXRFaceExpression>()->GetValueAsString(Expression));
|
||||
return;
|
||||
}
|
||||
|
||||
FName ExpressionName = ExpressionNames[Expression];
|
||||
MorphTargets.SetMorphTarget(ExpressionName, Value);
|
||||
}
|
||||
|
||||
float UOculusXRFaceTrackingComponent::GetExpressionValue(EOculusXRFaceExpression Expression) const
|
||||
{
|
||||
if (Expression >= EOculusXRFaceExpression::COUNT)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Cannot request expression value using an invalid expression index."));
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
FName ExpressionName = ExpressionNames[Expression];
|
||||
if (ExpressionName == NAME_None)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Cannot request expression value for an expression with an invalid associated morph target name. Expression name: %s"), *StaticEnum<EOculusXRFaceExpression>()->GetValueAsString(Expression));
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return MorphTargets.GetMorphTarget(ExpressionName);
|
||||
}
|
||||
|
||||
void UOculusXRFaceTrackingComponent::ClearExpressionValues()
|
||||
{
|
||||
MorphTargets.ClearMorphTargets();
|
||||
}
|
||||
|
||||
bool UOculusXRFaceTrackingComponent::InitializeFaceTracking()
|
||||
{
|
||||
TargetMeshComponent = OculusXRUtility::FindComponentByName<USkinnedMeshComponent>(GetOwner(), TargetMeshComponentName);
|
||||
|
||||
if (!IsValid(TargetMeshComponent))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Could not find skeletal mesh component with name: (%s). (%s:%s)"), *TargetMeshComponentName.ToString(), *GetOwner()->GetName(), *GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TargetMeshComponent != nullptr)
|
||||
{
|
||||
USkeletalMesh* TargetMesh = Cast<USkeletalMesh>(TargetMeshComponent->GetSkinnedAsset());
|
||||
if (TargetMesh != nullptr)
|
||||
{
|
||||
const TMap<FName, int32>& MorphTargetIndexMap = TargetMesh->GetMorphTargetIndexMap();
|
||||
|
||||
for (const auto& it : ExpressionNames)
|
||||
{
|
||||
ExpressionValid[static_cast<int32>(it.Key)] = MorphTargetIndexMap.Contains(it.Value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRLiveLinkRetargetBodyAsset.h"
|
||||
|
||||
#include "LiveLinkTypes.h"
|
||||
#include "Algo/Accumulate.h"
|
||||
#include "Algo/ForEach.h"
|
||||
#include "Roles/LiveLinkAnimationTypes.h"
|
||||
#include "BonePose.h"
|
||||
|
||||
#include "OculusXRHMDPrivate.h"
|
||||
#include "OculusXRMovementLog.h"
|
||||
#include "OculusXRMovement.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// EOculusXRAxis to orientation of that direction
|
||||
FTransform DirectionTransform(EOculusXRAxis Direction)
|
||||
{
|
||||
FVector Dir = FVector::ZeroVector;
|
||||
const uint8 IndexOfDir = static_cast<uint8>(Direction);
|
||||
const double Sign = IndexOfDir < static_cast<uint8>(EOculusXRAxis::NegativeX) ? 1 : -1;
|
||||
Dir[IndexOfDir % 3] = Sign * 1.0;
|
||||
return FTransform(Dir.ToOrientationQuat());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
UOculusXRLiveLinkRetargetBodyAsset::UOculusXRLiveLinkRetargetBodyAsset(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer), RetargetingMode(EOculusXRRetargetingMode::Full), ForwardMesh(EOculusXRAxis::X), Scale(100.f), TrackingSpaceToMeshSpace(FTransform::Identity), BoneNames(InPlace, NAME_None), LastBoneContainerSerialNumber(0)
|
||||
{
|
||||
}
|
||||
|
||||
void UOculusXRLiveLinkRetargetBodyAsset::Initialize()
|
||||
{
|
||||
TrackingSpaceToMeshSpace = DirectionTransform(ForwardTracking).Inverse() * DirectionTransform(ForwardMesh);
|
||||
GlobalBoneCorrection = FTransform(GlobalCorrection.RotationOffset, GlobalCorrection.PositionOffset);
|
||||
|
||||
for (uint8 BoneId = 0; BoneId < static_cast<uint8>(EOculusXRBoneID::COUNT); ++BoneId)
|
||||
{
|
||||
const FTransform LocalCorrectionCombined = Algo::Accumulate(LocalCorrections, FTransform::Identity, [BoneId](FTransform Correction, const FOculusXRBoneCorrectionSet& BoneCorrectionSet) {
|
||||
if (BoneCorrectionSet.Bones.Contains(static_cast<EOculusXRBoneID>(BoneId)))
|
||||
{
|
||||
Correction *= FTransform(BoneCorrectionSet.BoneCorrection.RotationOffset, BoneCorrectionSet.BoneCorrection.PositionOffset);
|
||||
}
|
||||
return Correction;
|
||||
});
|
||||
LocalBoneCorrections[BoneId] = LocalCorrectionCombined;
|
||||
|
||||
const EOculusXRBoneID OculusBoneID = static_cast<EOculusXRBoneID>(BoneId);
|
||||
if (const FName* NameMapping = BoneRemapping.Find(OculusBoneID))
|
||||
{
|
||||
BoneNames[BoneId] = *NameMapping;
|
||||
}
|
||||
else
|
||||
{
|
||||
BoneNames[BoneId] = NAME_None;
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Bone: %s isn't mapped."), *StaticEnum<EOculusXRBoneID>()->GetValueAsString(OculusBoneID));
|
||||
}
|
||||
}
|
||||
|
||||
if (!OculusXRHMD::GetUnitScaleFactorFromSettings(UObject::GetWorld(), Scale))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Cannot get world settings for body retargetting asset."));
|
||||
}
|
||||
|
||||
LastBoneContainerSerialNumber = 0;
|
||||
Algo::ForEach(LastSkeletonBoneRemapping, [](FCompactPoseBoneIndex& BoneIndex) { BoneIndex = FCompactPoseBoneIndex(INDEX_NONE); });
|
||||
}
|
||||
|
||||
void UOculusXRLiveLinkRetargetBodyAsset::BuildPoseFromAnimationData(float DeltaTime, const FLiveLinkSkeletonStaticData* InSkeletonData, const FLiveLinkAnimationFrameData* InFrameData, FCompactPose& OutPose)
|
||||
{
|
||||
check(InFrameData);
|
||||
if (static_cast<int32>(EOculusXRBoneID::COUNT) != InFrameData->Transforms.Num())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Error, TEXT("Received wrong data of live link frame. This retargeting asset must be used with Meta MovementSDK Live Link source and Body subject. (received %d bone transforms, expected %d)"), InFrameData->Transforms.Num(), static_cast<int32>(EOculusXRBoneID::COUNT));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((LastBoneContainerSerialNumber != OutPose.GetBoneContainer().GetSerialNumber()) || (LastBoneContainerSerialNumber == 0))
|
||||
{
|
||||
OnBoneContainerChanged(OutPose.GetBoneContainer());
|
||||
}
|
||||
|
||||
FCSPose<FCompactPose> MeshPoses;
|
||||
MeshPoses.InitPose(OutPose);
|
||||
for (uint8 BoneId = 0; BoneId < static_cast<uint8>(EOculusXRBoneID::COUNT); ++BoneId)
|
||||
{
|
||||
if (const FCompactPoseBoneIndex& BoneIndex = LastSkeletonBoneRemapping[BoneId]; BoneIndex != INDEX_NONE)
|
||||
{
|
||||
FTransform BoneTransform = InFrameData->Transforms[BoneId];
|
||||
BoneTransform.ScaleTranslation(Scale);
|
||||
BoneTransform *= TrackingSpaceToMeshSpace;
|
||||
BoneTransform = GlobalBoneCorrection * BoneTransform;
|
||||
BoneTransform = LocalBoneCorrections[BoneId] * BoneTransform;
|
||||
check(!BoneTransform.ContainsNaN());
|
||||
|
||||
switch (RetargetingMode)
|
||||
{
|
||||
case EOculusXRRetargetingMode::Rotations:
|
||||
BoneTransform.SetLocation(MeshPoses.GetComponentSpaceTransform(BoneIndex).GetLocation());
|
||||
MeshPoses.SetComponentSpaceTransform(BoneIndex, BoneTransform);
|
||||
break;
|
||||
|
||||
case EOculusXRRetargetingMode::RotationsPlusRoot:
|
||||
if (BoneId != static_cast<uint8>(EOculusXRBoneID::BodyRoot))
|
||||
{
|
||||
BoneTransform.SetLocation(MeshPoses.GetComponentSpaceTransform(BoneIndex).GetLocation());
|
||||
}
|
||||
MeshPoses.SetComponentSpaceTransform(BoneIndex, BoneTransform);
|
||||
break;
|
||||
|
||||
case EOculusXRRetargetingMode::RotationsPlusHips:
|
||||
if (BoneId != static_cast<uint8>(EOculusXRBoneID::BodyHips))
|
||||
{
|
||||
BoneTransform.SetLocation(MeshPoses.GetComponentSpaceTransform(BoneIndex).GetLocation());
|
||||
}
|
||||
MeshPoses.SetComponentSpaceTransform(BoneIndex, BoneTransform);
|
||||
break;
|
||||
|
||||
case EOculusXRRetargetingMode::Full:
|
||||
MeshPoses.SetComponentSpaceTransform(BoneIndex, BoneTransform);
|
||||
break;
|
||||
|
||||
case EOculusXRRetargetingMode::None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
FCSPose<FCompactPose>::ConvertComponentPosesToLocalPosesSafe(MeshPoses, OutPose);
|
||||
}
|
||||
|
||||
void UOculusXRLiveLinkRetargetBodyAsset::OnBoneContainerChanged(const FBoneContainer& BoneContainer)
|
||||
{
|
||||
LastBoneContainerSerialNumber = 0;
|
||||
|
||||
for (uint8 BoneId = 0; BoneId < static_cast<uint8>(EOculusXRBoneID::COUNT); ++BoneId)
|
||||
{
|
||||
const auto& BoneName = BoneNames[BoneId];
|
||||
if (BoneName.IsNone())
|
||||
{
|
||||
LastSkeletonBoneRemapping[BoneId] = FCompactPoseBoneIndex(INDEX_NONE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const int32 MeshIndex = BoneContainer.GetPoseBoneIndexForBoneName(BoneName); MeshIndex != INDEX_NONE)
|
||||
{
|
||||
LastSkeletonBoneRemapping[BoneId] = BoneContainer.MakeCompactPoseIndex(FMeshPoseBoneIndex(MeshIndex));
|
||||
if (LastSkeletonBoneRemapping[BoneId] == INDEX_NONE)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("Bone %s was intentionally mapped to %s. But this target doesn't exist in skeleton."), *StaticEnum<EOculusXRBoneID>()->GetValueAsString(static_cast<EOculusXRBoneID>(BoneId)), *BoneName.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LastBoneContainerSerialNumber = BoneContainer.GetSerialNumber();
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRLiveLinkRetargetFaceAsset.h"
|
||||
|
||||
#include "LiveLinkTypes.h"
|
||||
#include "Algo/ForEach.h"
|
||||
#include "Animation/AnimCurveTypes.h"
|
||||
#include "BonePose.h"
|
||||
#include "OculusXRMovement.h"
|
||||
#include "OculusXRMovementLog.h"
|
||||
|
||||
UOculusXRLiveLinkRetargetFaceAsset::UOculusXRLiveLinkRetargetFaceAsset(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
void UOculusXRLiveLinkRetargetFaceAsset::Initialize()
|
||||
{
|
||||
LastSkeletonGuid.Invalidate();
|
||||
#if UE_VERSION_OLDER_THAN(5, 3, 0)
|
||||
Algo::ForEach(RemappingForLastSkeleton, [](TArray<SmartName::UID_Type>& Arr) { Arr.Reset(); });
|
||||
#else
|
||||
Algo::ForEach(RemappingForLastSkeleton, [](TArray<FName>& Arr) { Arr.Reset(); });
|
||||
#endif
|
||||
}
|
||||
|
||||
void UOculusXRLiveLinkRetargetFaceAsset::BuildPoseAndCurveFromBaseData(float DeltaTime, const FLiveLinkBaseStaticData* InBaseStaticData, const FLiveLinkBaseFrameData* InBaseFrameData, FCompactPose& OutPose, FBlendedCurve& OutCurve)
|
||||
{
|
||||
check(InBaseFrameData);
|
||||
if (static_cast<int32>(EOculusXRFaceExpression::COUNT) != InBaseFrameData->PropertyValues.Num())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Error, TEXT("Received wrong data of live link frame. This retargeting asset must be used with Meta MovementSDK Live Link source and Face subject. (received %d face expressions, expected %d)"), InBaseFrameData->PropertyValues.Num(), static_cast<int32>(EOculusXRFaceExpression::COUNT));
|
||||
return;
|
||||
}
|
||||
const USkeleton* Skeleton = OutPose.GetBoneContainer().GetSkeletonAsset();
|
||||
if (!IsValid(Skeleton))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Error, TEXT("No skeleton asset for this retargeting."));
|
||||
return;
|
||||
}
|
||||
if (LastSkeletonGuid != Skeleton->GetGuid())
|
||||
{
|
||||
OnSkeletonChanged(Skeleton);
|
||||
}
|
||||
|
||||
for (uint8 ExpressionId = 0; ExpressionId < static_cast<uint8>(EOculusXRFaceExpression::COUNT); ++ExpressionId)
|
||||
{
|
||||
#if UE_VERSION_OLDER_THAN(5, 3, 0)
|
||||
for (const SmartName::UID_Type UID : RemappingForLastSkeleton[ExpressionId])
|
||||
{
|
||||
OutCurve.Set(UID, InBaseFrameData->PropertyValues[ExpressionId]);
|
||||
}
|
||||
#else
|
||||
for (const FName Name : RemappingForLastSkeleton[ExpressionId])
|
||||
{
|
||||
OutCurve.Set(Name, InBaseFrameData->PropertyValues[ExpressionId]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRLiveLinkRetargetFaceAsset::OnSkeletonChanged(const USkeleton* Skeleton)
|
||||
{
|
||||
Initialize();
|
||||
|
||||
for (const auto& [ExpressionId, CurveMapping] : CurveRemapping)
|
||||
{
|
||||
for (const auto& CurveName : CurveMapping.CurveNames)
|
||||
{
|
||||
#if UE_VERSION_OLDER_THAN(5, 3, 0)
|
||||
if (const SmartName::UID_Type UID = Skeleton->GetUIDByName(USkeleton::AnimCurveMappingName, CurveName); UID != SmartName::MaxUID)
|
||||
{
|
||||
RemappingForLastSkeleton[static_cast<uint8>(ExpressionId)].Emplace(UID);
|
||||
}
|
||||
#else
|
||||
RemappingForLastSkeleton[static_cast<uint8>(ExpressionId)].Emplace(CurveName);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
LastSkeletonGuid = Skeleton->GetGuid();
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRMorphTargetsController.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
|
||||
#include "AnimationRuntime.h"
|
||||
|
||||
void FOculusXRMorphTargetsController::ResetMorphTargetCurves(USkinnedMeshComponent* TargetMeshComponent)
|
||||
{
|
||||
if (TargetMeshComponent)
|
||||
{
|
||||
TargetMeshComponent->ActiveMorphTargets.Reset();
|
||||
|
||||
USkeletalMesh* TargetMesh = Cast<USkeletalMesh>(TargetMeshComponent->GetSkinnedAsset());
|
||||
if (TargetMesh != nullptr)
|
||||
{
|
||||
TargetMeshComponent->MorphTargetWeights.SetNum(TargetMesh->GetMorphTargets().Num());
|
||||
|
||||
// we need this code to ensure the buffer gets cleared whether or not you have morphtarget curve set
|
||||
// the case, where you had morphtargets weight on, and when you clear the weight, you want to make sure
|
||||
// the buffer gets cleared and resized
|
||||
if (TargetMeshComponent->MorphTargetWeights.Num() > 0)
|
||||
{
|
||||
FMemory::Memzero(TargetMeshComponent->MorphTargetWeights.GetData(), TargetMeshComponent->MorphTargetWeights.GetAllocatedSize());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TargetMeshComponent->MorphTargetWeights.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRMorphTargetsController::ApplyMorphTargets(USkinnedMeshComponent* TargetMeshComponent)
|
||||
{
|
||||
if (TargetMeshComponent != nullptr)
|
||||
{
|
||||
const USkeletalMesh* TargetMesh = Cast<USkeletalMesh>(TargetMeshComponent->GetSkinnedAsset());
|
||||
if (TargetMesh != nullptr && MorphTargetCurves.Num() > 0)
|
||||
{
|
||||
FAnimationRuntime::AppendActiveMorphTargets(TargetMesh, MorphTargetCurves, TargetMeshComponent->ActiveMorphTargets, TargetMeshComponent->MorphTargetWeights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRMorphTargetsController::SetMorphTarget(FName MorphTargetName, float Value)
|
||||
{
|
||||
float* CurveValPtr = MorphTargetCurves.Find(MorphTargetName);
|
||||
bool bShouldAddToList = FPlatformMath::Abs(Value) > ZERO_ANIMWEIGHT_THRESH;
|
||||
if (bShouldAddToList)
|
||||
{
|
||||
if (CurveValPtr)
|
||||
{
|
||||
// sum up, in the future we might normalize, but for now this just sums up
|
||||
// this won't work well if all of them have full weight - i.e. additive
|
||||
*CurveValPtr = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
MorphTargetCurves.Add(MorphTargetName, Value);
|
||||
}
|
||||
}
|
||||
// if less than ZERO_ANIMWEIGHT_THRESH
|
||||
// no reason to keep them on the list
|
||||
else
|
||||
{
|
||||
// remove if found
|
||||
MorphTargetCurves.Remove(MorphTargetName);
|
||||
}
|
||||
}
|
||||
|
||||
float FOculusXRMorphTargetsController::GetMorphTarget(FName MorphTargetName) const
|
||||
{
|
||||
const float* CurveValPtr = MorphTargetCurves.Find(MorphTargetName);
|
||||
|
||||
if (CurveValPtr)
|
||||
{
|
||||
return *CurveValPtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRMorphTargetsController::ClearMorphTargets()
|
||||
{
|
||||
MorphTargetCurves.Empty();
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRMovement.h"
|
||||
#include "OculusXRMovementLog.h"
|
||||
#include "OculusXRMovementModule.h"
|
||||
#include "OculusXRHMDPrivate.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "OculusXRPluginWrapper.h"
|
||||
#include "Logging/MessageLog.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMovement"
|
||||
|
||||
bool OculusXRMovement::IsFullBodyTrackingEnabled()
|
||||
{
|
||||
bool bResult = false;
|
||||
|
||||
ovrpBool IsEnabled = ovrpBool_False;
|
||||
ovrpResult TrackingEnabledResult = FOculusXRHMDModule::GetPluginWrapper().GetFullBodyTrackingEnabled(&IsEnabled);
|
||||
|
||||
if (OVRP_SUCCESS(TrackingEnabledResult))
|
||||
{
|
||||
bResult = (IsEnabled == ovrpBool_True);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::GetBodyState(FOculusXRBodyState& outOculusXRBodyState, float WorldToMeters)
|
||||
{
|
||||
static_assert(ovrpBoneId_FullBody_End == static_cast<int>(EOculusXRBoneID::COUNT), "The size of the OVRPlugin Bone ID enum should be the same as the EOculusXRBoneID enum.");
|
||||
|
||||
const auto AvailableJoints = IsFullBodyTrackingEnabled() ? ovrpBoneId_FullBody_End : ovrpBoneId_Body_End;
|
||||
checkf(outOculusXRBodyState.Joints.Num() >= AvailableJoints, TEXT("Not enough joints in FOculusXRBodyState::Joints array. You must have at least %d joints"), AvailableJoints);
|
||||
|
||||
ovrpBodyState4 OVRBodyState;
|
||||
ovrpResult OVRBodyStateResult = FOculusXRHMDModule::GetPluginWrapper().GetBodyState4(ovrpStep_Render, OVRP_CURRENT_FRAMEINDEX, &OVRBodyState);
|
||||
ensureMsgf(OVRBodyStateResult != ovrpFailure_NotYetImplemented, TEXT("Body tracking is not implemented on this platform."));
|
||||
|
||||
if (OVRP_SUCCESS(OVRBodyStateResult))
|
||||
{
|
||||
outOculusXRBodyState.IsActive = (OVRBodyState.IsActive == ovrpBool_True);
|
||||
outOculusXRBodyState.Confidence = OVRBodyState.Confidence;
|
||||
outOculusXRBodyState.SkeletonChangedCount = OVRBodyState.SkeletonChangedCount;
|
||||
outOculusXRBodyState.Time = static_cast<float>(OVRBodyState.Time);
|
||||
|
||||
for (int i = 0; i < AvailableJoints; ++i)
|
||||
{
|
||||
ovrpBodyJointLocation OVRJointLocation = OVRBodyState.JointLocations[i];
|
||||
ovrpPosef OVRJointPose = OVRJointLocation.Pose;
|
||||
|
||||
FOculusXRBodyJoint& OculusXRBodyJoint = outOculusXRBodyState.Joints[i];
|
||||
OculusXRBodyJoint.LocationFlags = OVRJointLocation.LocationFlags;
|
||||
OculusXRBodyJoint.bIsValid = OVRJointLocation.LocationFlags & (XRSpaceFlags::XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XRSpaceFlags::XR_SPACE_LOCATION_POSITION_VALID_BIT);
|
||||
OculusXRBodyJoint.Orientation = FRotator(OculusXRHMD::ToFQuat(OVRJointPose.Orientation));
|
||||
OculusXRBodyJoint.Position = OculusXRHMD::ToFVector(OVRJointPose.Position) * WorldToMeters;
|
||||
}
|
||||
if (AvailableJoints < outOculusXRBodyState.Joints.Num())
|
||||
{
|
||||
for (int i = AvailableJoints; i < outOculusXRBodyState.Joints.Num(); ++i)
|
||||
{
|
||||
outOculusXRBodyState.Joints[i].bIsValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool OculusXRMovement::IsBodyTrackingEnabled()
|
||||
{
|
||||
bool bResult = false;
|
||||
|
||||
ovrpBool IsEnabled = ovrpBool_False;
|
||||
ovrpResult TrackingEnabledResult = FOculusXRHMDModule::GetPluginWrapper().GetBodyTrackingEnabled(&IsEnabled);
|
||||
|
||||
if (OVRP_SUCCESS(TrackingEnabledResult))
|
||||
{
|
||||
bResult = (IsEnabled == ovrpBool_True);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::IsBodyTrackingSupported()
|
||||
{
|
||||
bool bResult = false;
|
||||
|
||||
ovrpBool IsSupported = ovrpBool_False;
|
||||
ovrpResult TrackingSupportedResult = FOculusXRHMDModule::GetPluginWrapper().GetBodyTrackingSupported(&IsSupported);
|
||||
|
||||
if (OVRP_SUCCESS(TrackingSupportedResult))
|
||||
{
|
||||
bResult = (IsSupported == ovrpBool_True);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::RequestBodyTrackingFidelity(EOculusXRBodyTrackingFidelity fidelity)
|
||||
{
|
||||
static_assert(static_cast<int>(EOculusXRBodyTrackingFidelity::Low) == static_cast<int>(ovrpBodyTrackingFidelity2::ovrpBodyTrackingFidelity2_Low), "EOculusXRBodyTrackingFidelity and ovrpBodyTrackingFidelity2 should be sync");
|
||||
static_assert(static_cast<int>(EOculusXRBodyTrackingFidelity::High) == static_cast<int>(ovrpBodyTrackingFidelity2::ovrpBodyTrackingFidelity2_High), "EOculusXRBodyTrackingFidelity and ovrpBodyTrackingFidelity2 should be sync");
|
||||
|
||||
auto* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
|
||||
if (OculusXRHMD)
|
||||
{
|
||||
OculusXRHMD->GetSettings()->BodyTrackingFidelity = static_cast<EOculusXRHMDBodyTrackingFidelity>(fidelity);
|
||||
}
|
||||
|
||||
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().RequestBodyTrackingFidelity(static_cast<ovrpBodyTrackingFidelity2>(fidelity)));
|
||||
}
|
||||
|
||||
bool OculusXRMovement::ResetBodyTrackingCalibration()
|
||||
{
|
||||
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().ResetBodyTrackingCalibration());
|
||||
}
|
||||
|
||||
bool OculusXRMovement::SuggestBodyTrackingCalibrationOverride(float height)
|
||||
{
|
||||
ovrpBodyTrackingCalibrationInfo calibrationInfo{ height };
|
||||
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().SuggestBodyTrackingCalibrationOverride(calibrationInfo));
|
||||
}
|
||||
|
||||
bool OculusXRMovement::StartBodyTracking()
|
||||
{
|
||||
static_assert(static_cast<int>(EOculusXRBodyJointSet::UpperBody) == static_cast<int>(ovrpBodyJointSet::ovrpBodyJointSet_UpperBody), "EOculusXRBodyJointSet and ovrpBodyJointSet should be sync");
|
||||
static_assert(static_cast<int>(EOculusXRBodyJointSet::FullBody) == static_cast<int>(ovrpBodyJointSet::ovrpBodyJointSet_FullBody), "EOculusXRBodyJointSet and ovrpBodyJointSet should be sync");
|
||||
|
||||
bool result = false;
|
||||
|
||||
const auto* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
|
||||
if (OculusXRHMD)
|
||||
{
|
||||
const auto JointSet = OculusXRHMD->GetSettings()->BodyTrackingJointSet;
|
||||
if (!OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().StartBodyTracking2(static_cast<ovrpBodyJointSet>(JointSet))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto Fidelity = OculusXRHMD->GetSettings()->BodyTrackingFidelity;
|
||||
FOculusXRHMDModule::GetPluginWrapper().RequestBodyTrackingFidelity(static_cast<ovrpBodyTrackingFidelity2>(Fidelity));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().StartBodyTracking2(ovrpBodyJointSet::ovrpBodyJointSet_UpperBody));
|
||||
}
|
||||
}
|
||||
|
||||
bool OculusXRMovement::StartBodyTrackingByJointSet(EOculusXRBodyJointSet jointSet)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
auto* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
|
||||
if (OculusXRHMD)
|
||||
{
|
||||
OculusXRHMD->GetSettings()->BodyTrackingJointSet = static_cast<EOculusXRHMDBodyJointSet>(jointSet);
|
||||
result = StartBodyTracking();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().StartBodyTracking2(static_cast<ovrpBodyJointSet>(jointSet)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::StopBodyTracking()
|
||||
{
|
||||
FOculusXRHMDModule::GetPluginWrapper().RequestBodyTrackingFidelity(ovrpBodyTrackingFidelity2::ovrpBodyTrackingFidelity2_Low);
|
||||
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().StopBodyTracking());
|
||||
}
|
||||
|
||||
bool OculusXRMovement::GetFaceState(FOculusXRFaceState& outOculusXRFaceState)
|
||||
{
|
||||
const auto blendShapeCount = ovrpFaceExpression2_Max;
|
||||
|
||||
static_assert(blendShapeCount == static_cast<int>(EOculusXRFaceExpression::COUNT), "The size of the OVRPlugin Face Expression enum should be the same as the EOculusXRFaceExpression enum.");
|
||||
|
||||
checkf(outOculusXRFaceState.ExpressionWeightConfidences.Num() >= ovrpFaceConfidence_Max, TEXT("Not enough expression weight confidences in FOculusXRFaceState::ExpressionWeightConfidences. Requires %d available elements in the array."), ovrpFaceConfidence_Max);
|
||||
checkf(outOculusXRFaceState.ExpressionWeights.Num() >= blendShapeCount, TEXT("Not enough expression weights in FOculusXRFaceState::ExpressionWeights. Requires %d available elements in the array."), blendShapeCount);
|
||||
|
||||
ovrpFaceState2 OVRFaceState;
|
||||
ovrpResult OVRFaceStateResult = FOculusXRHMDModule::GetPluginWrapper().GetFaceState2(ovrpStep_Render, OVRP_CURRENT_FRAMEINDEX, &OVRFaceState);
|
||||
|
||||
ensureMsgf(OVRFaceStateResult != ovrpFailure_NotYetImplemented, TEXT("Face tracking is not implemented on this platform."));
|
||||
|
||||
if (OVRP_SUCCESS(OVRFaceStateResult))
|
||||
{
|
||||
outOculusXRFaceState.bIsValid = (OVRFaceState.Status.IsValid == ovrpBool_True);
|
||||
outOculusXRFaceState.bIsEyeFollowingBlendshapesValid = (OVRFaceState.Status.IsEyeFollowingBlendshapesValid == ovrpBool_True);
|
||||
outOculusXRFaceState.Time = static_cast<float>(OVRFaceState.Time);
|
||||
|
||||
for (int i = 0; i < blendShapeCount; ++i)
|
||||
{
|
||||
outOculusXRFaceState.ExpressionWeights[i] = OVRFaceState.ExpressionWeights[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < ovrpFaceConfidence_Max; ++i)
|
||||
{
|
||||
outOculusXRFaceState.ExpressionWeightConfidences[i] = OVRFaceState.ExpressionWeightConfidences[i];
|
||||
}
|
||||
|
||||
outOculusXRFaceState.DataSource = static_cast<EFaceTrackingDataSource>(OVRFaceState.DataSource);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::IsFaceTrackingEnabled()
|
||||
{
|
||||
bool bResult = false;
|
||||
|
||||
ovrpBool IsEnabled = ovrpBool_False;
|
||||
ovrpResult TrackingEnabledResult = FOculusXRHMDModule::GetPluginWrapper().GetFaceTracking2Enabled(&IsEnabled);
|
||||
|
||||
if (OVRP_SUCCESS(TrackingEnabledResult))
|
||||
{
|
||||
bResult = (IsEnabled == ovrpBool_True);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::IsFaceTrackingSupported()
|
||||
{
|
||||
bool bResult = false;
|
||||
|
||||
ovrpBool IsSupported = ovrpBool_False;
|
||||
ovrpResult TrackingSupportedResult = FOculusXRHMDModule::GetPluginWrapper().GetFaceTracking2Supported(&IsSupported);
|
||||
|
||||
if (OVRP_SUCCESS(TrackingSupportedResult))
|
||||
{
|
||||
bResult = (IsSupported == ovrpBool_True);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::StartFaceTracking()
|
||||
{
|
||||
const auto* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
|
||||
if (OculusXRHMD)
|
||||
{
|
||||
ovrpFaceTrackingDataSource2 dataSources[ovrpFaceConstants_FaceTrackingDataSourcesCount];
|
||||
int count = 0;
|
||||
for (auto Iterator = OculusXRHMD->GetSettings()->FaceTrackingDataSource.CreateConstIterator(); Iterator; ++Iterator)
|
||||
{
|
||||
dataSources[count++] = static_cast<ovrpFaceTrackingDataSource2>(*Iterator);
|
||||
}
|
||||
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().StartFaceTracking2(dataSources, count));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::StopFaceTracking()
|
||||
{
|
||||
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().StopFaceTracking2());
|
||||
}
|
||||
|
||||
bool OculusXRMovement::GetEyeGazesState(FOculusXREyeGazesState& outOculusXREyeGazesState, float WorldToMeters)
|
||||
{
|
||||
static_assert(ovrpEye_Count == (int)EOculusXREye::COUNT, "The size of the OVRPlugin Eye enum should be the same as the EOculusXREye enum.");
|
||||
|
||||
checkf(outOculusXREyeGazesState.EyeGazes.Num() >= ovrpEye_Count, TEXT("Not enough eye gaze states in FOculusXREyeGazesState::EyeGazes. Requires %d available elements in the array."), ovrpEye_Count);
|
||||
|
||||
ovrpEyeGazesState OVREyeGazesState;
|
||||
ovrpResult OVREyeGazesStateResult = FOculusXRHMDModule::GetPluginWrapper().GetEyeGazesState(ovrpStep_Render, OVRP_CURRENT_FRAMEINDEX, &OVREyeGazesState);
|
||||
ensureMsgf(OVREyeGazesStateResult != ovrpFailure_NotYetImplemented, TEXT("Eye tracking is not implemented on this platform."));
|
||||
|
||||
if (OVRP_SUCCESS(OVREyeGazesStateResult))
|
||||
{
|
||||
outOculusXREyeGazesState.Time = static_cast<float>(OVREyeGazesState.Time);
|
||||
for (int i = 0; i < ovrpEye_Count; ++i)
|
||||
{
|
||||
const auto& EyeGazePose = OVREyeGazesState.EyeGazes[i].Pose;
|
||||
outOculusXREyeGazesState.EyeGazes[i].Orientation = FRotator(OculusXRHMD::ToFQuat(EyeGazePose.Orientation));
|
||||
outOculusXREyeGazesState.EyeGazes[i].Position = OculusXRHMD::ToFVector(EyeGazePose.Position) * WorldToMeters;
|
||||
outOculusXREyeGazesState.EyeGazes[i].bIsValid = (OVREyeGazesState.EyeGazes[i].IsValid == ovrpBool_True);
|
||||
outOculusXREyeGazesState.EyeGazes[i].Confidence = OVREyeGazesState.EyeGazes[i].Confidence;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::IsEyeTrackingEnabled()
|
||||
{
|
||||
bool bResult = false;
|
||||
|
||||
ovrpBool IsEnabled = ovrpBool_False;
|
||||
ovrpResult TrackingEnabledResult = FOculusXRHMDModule::GetPluginWrapper().GetEyeTrackingEnabled(&IsEnabled);
|
||||
|
||||
if (OVRP_SUCCESS(TrackingEnabledResult))
|
||||
{
|
||||
bResult = (IsEnabled == ovrpBool_True);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::IsEyeTrackingSupported()
|
||||
{
|
||||
bool bResult = false;
|
||||
|
||||
ovrpBool IsSupported = ovrpBool_False;
|
||||
ovrpResult TrackingSupportedResult = FOculusXRHMDModule::GetPluginWrapper().GetEyeTrackingSupported(&IsSupported);
|
||||
|
||||
if (OVRP_SUCCESS(TrackingSupportedResult))
|
||||
{
|
||||
bResult = (IsSupported == ovrpBool_True);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool OculusXRMovement::StartEyeTracking()
|
||||
{
|
||||
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().StartEyeTracking());
|
||||
}
|
||||
|
||||
bool OculusXRMovement::StopEyeTracking()
|
||||
{
|
||||
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().StopEyeTracking());
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRMovementFunctionLibrary.h"
|
||||
|
||||
#include "IOculusXRMovementModule.h"
|
||||
#include "LiveLinkOculusXRMovementSourceFactory.h"
|
||||
#include "OculusXRHMDPrivate.h"
|
||||
#include "OculusXRMovement.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "OculusXRMovementLiveLink.h"
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::TryGetBodyState(FOculusXRBodyState& outBodyState, float WorldToMeters)
|
||||
{
|
||||
return OculusXRMovement::GetBodyState(outBodyState, WorldToMeters);
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::IsBodyTrackingEnabled()
|
||||
{
|
||||
return OculusXRMovement::IsBodyTrackingEnabled();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::IsBodyTrackingSupported()
|
||||
{
|
||||
return OculusXRMovement::IsBodyTrackingSupported();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::RequestBodyTrackingFidelity(EOculusXRBodyTrackingFidelity fidelity)
|
||||
{
|
||||
return OculusXRMovement::RequestBodyTrackingFidelity(fidelity);
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::ResetBodyTrackingCalibration()
|
||||
{
|
||||
return OculusXRMovement::ResetBodyTrackingCalibration();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::SuggestBodyTrackingCalibrationOverride(float height)
|
||||
{
|
||||
return OculusXRMovement::SuggestBodyTrackingCalibrationOverride(height);
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::StartBodyTrackingByJointSet(EOculusXRBodyJointSet jointSet)
|
||||
{
|
||||
return OculusXRMovement::StartBodyTrackingByJointSet(jointSet);
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::StartBodyTracking()
|
||||
{
|
||||
return OculusXRMovement::StartBodyTracking();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::StopBodyTracking()
|
||||
{
|
||||
return OculusXRMovement::StopBodyTracking();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::TryGetFaceState(FOculusXRFaceState& outFaceState)
|
||||
{
|
||||
return OculusXRMovement::GetFaceState(outFaceState);
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::IsFaceTrackingEnabled()
|
||||
{
|
||||
return OculusXRMovement::IsFaceTrackingEnabled();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::IsFaceTrackingSupported()
|
||||
{
|
||||
return OculusXRMovement::IsFaceTrackingSupported();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::StartFaceTracking()
|
||||
{
|
||||
return OculusXRMovement::StartFaceTracking();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::StopFaceTracking()
|
||||
{
|
||||
return OculusXRMovement::StopFaceTracking();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::TryGetEyeGazesState(FOculusXREyeGazesState& outEyeGazesState, float WorldToMeters)
|
||||
{
|
||||
return OculusXRMovement::GetEyeGazesState(outEyeGazesState, WorldToMeters);
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::IsEyeTrackingEnabled()
|
||||
{
|
||||
return OculusXRMovement::IsEyeTrackingEnabled();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::IsEyeTrackingSupported()
|
||||
{
|
||||
return OculusXRMovement::IsEyeTrackingSupported();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::StartEyeTracking()
|
||||
{
|
||||
return OculusXRMovement::StartEyeTracking();
|
||||
}
|
||||
|
||||
bool UOculusXRMovementFunctionLibrary::StopEyeTracking()
|
||||
{
|
||||
return OculusXRMovement::StopEyeTracking();
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRMovementLiveLink.h"
|
||||
|
||||
#include "IHeadMountedDisplayModule.h"
|
||||
#include "OculusXRHMDModule.h"
|
||||
#include "OculusXRMovementLog.h"
|
||||
#include "OculusXRMovement.h"
|
||||
#include "OculusXRMovementTypes.h"
|
||||
#include "OculusXRTelemetryMovementEvents.h"
|
||||
|
||||
#include "Roles/LiveLinkAnimationTypes.h"
|
||||
#include "ILiveLinkClient.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "MetaOculusXRMovement"
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int32 NoParent = -1;
|
||||
}
|
||||
|
||||
namespace MetaXRMovement
|
||||
{
|
||||
template <>
|
||||
void FEyeSubject::InitializeRoleStaticData(FLiveLinkSkeletonStaticData& StaticData) const
|
||||
{
|
||||
constexpr auto FieldsCount = static_cast<uint8>(EOculusXREye::COUNT);
|
||||
StaticData.BoneNames.Reserve(FieldsCount);
|
||||
for (uint8 XRBone = 0; XRBone < FieldsCount; ++XRBone)
|
||||
{
|
||||
StaticData.BoneNames.Add(UEnum::GetValueAsName(static_cast<EOculusXREye>(XRBone)));
|
||||
StaticData.BoneParents.Add(NoParent);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void FBodySubject::InitializeRoleStaticData(FLiveLinkSkeletonStaticData& StaticData) const
|
||||
{
|
||||
constexpr auto FieldsCount = static_cast<uint8>(EOculusXRBoneID::COUNT);
|
||||
StaticData.BoneNames.Reserve(FieldsCount);
|
||||
for (uint8 XRBone = 0; XRBone < FieldsCount; ++XRBone)
|
||||
{
|
||||
StaticData.BoneNames.Add(UEnum::GetValueAsName(static_cast<EOculusXRBoneID>(XRBone)));
|
||||
StaticData.BoneParents.Add(NoParent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
void FFaceSubject::InitializeRoleStaticData(FLiveLinkBaseStaticData& StaticData) const
|
||||
{
|
||||
constexpr auto FieldsCount = static_cast<uint8>(EOculusXRFaceExpression::COUNT);
|
||||
StaticData.PropertyNames.Reserve(FieldsCount);
|
||||
for (uint8 XRProperty = 0; XRProperty < FieldsCount; ++XRProperty)
|
||||
{
|
||||
StaticData.PropertyNames.Add(UEnum::GetValueAsName(static_cast<EOculusXRFaceExpression>(XRProperty)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename MetaXRState, typename RoleTypeStaticData, typename RoleTypeFrameData, typename Role>
|
||||
FLiveLinkStaticDataStruct TSubject<MetaXRState, RoleTypeStaticData, RoleTypeFrameData, Role>::StaticData() const
|
||||
{
|
||||
FLiveLinkStaticDataStruct StaticDataStruct(RoleTypeStaticData::StaticStruct());
|
||||
RoleTypeStaticData& RoleStaticData(*StaticDataStruct.Cast<RoleTypeStaticData>());
|
||||
InitializeRoleStaticData(RoleStaticData);
|
||||
return StaticDataStruct;
|
||||
}
|
||||
template <typename MetaXRState, typename RoleTypeStaticData, typename RoleTypeFrameData, typename Role>
|
||||
FLiveLinkFrameDataStruct TSubject<MetaXRState, RoleTypeStaticData, RoleTypeFrameData, Role>::FrameData()
|
||||
{
|
||||
FLiveLinkFrameDataStruct FrameDataStruct(RoleTypeFrameData::StaticStruct());
|
||||
RoleTypeFrameData& FrameData(*FrameDataStruct.Cast<RoleTypeFrameData>());
|
||||
UpdateFrame(FrameData);
|
||||
return FrameDataStruct;
|
||||
}
|
||||
|
||||
template <>
|
||||
FEyeSubject::TSubject()
|
||||
: Name(TEXT("Eye"))
|
||||
, bLastFrameIsValid(false)
|
||||
, bStarted(false)
|
||||
{
|
||||
}
|
||||
template <>
|
||||
FFaceSubject::TSubject()
|
||||
: Name(TEXT("Face"))
|
||||
, bLastFrameIsValid(false)
|
||||
, bStarted(false)
|
||||
{
|
||||
}
|
||||
template <>
|
||||
FBodySubject::TSubject()
|
||||
: Name(TEXT("Body"))
|
||||
, bLastFrameIsValid(false)
|
||||
, bStarted(false)
|
||||
{
|
||||
}
|
||||
template <>
|
||||
bool FEyeSubject::Start()
|
||||
{
|
||||
if (!bStarted)
|
||||
{
|
||||
bStarted = OculusXRMovement::StartEyeTracking();
|
||||
}
|
||||
return bStarted;
|
||||
}
|
||||
template <>
|
||||
bool FEyeSubject::Stop()
|
||||
{
|
||||
if (bStarted)
|
||||
{
|
||||
bStarted = !OculusXRMovement::StopEyeTracking();
|
||||
}
|
||||
return !bStarted;
|
||||
}
|
||||
template <>
|
||||
bool FFaceSubject::Start()
|
||||
{
|
||||
if (!bStarted)
|
||||
{
|
||||
bStarted = OculusXRMovement::StartFaceTracking();
|
||||
}
|
||||
return bStarted;
|
||||
}
|
||||
template <>
|
||||
bool FFaceSubject::Stop()
|
||||
{
|
||||
if (bStarted)
|
||||
{
|
||||
bStarted = !OculusXRMovement::StopFaceTracking();
|
||||
}
|
||||
return !bStarted;
|
||||
}
|
||||
template <>
|
||||
bool FBodySubject::Start()
|
||||
{
|
||||
if (!bStarted)
|
||||
{
|
||||
bStarted = OculusXRMovement::StartBodyTracking();
|
||||
}
|
||||
return bStarted;
|
||||
}
|
||||
template <>
|
||||
bool FBodySubject::Stop()
|
||||
{
|
||||
if (bStarted)
|
||||
{
|
||||
bStarted = !OculusXRMovement::StopBodyTracking();
|
||||
}
|
||||
return !bStarted;
|
||||
}
|
||||
template <>
|
||||
bool FEyeSubject::IsSupported()
|
||||
{
|
||||
return OculusXRMovement::IsEyeTrackingSupported();
|
||||
}
|
||||
template <>
|
||||
bool FFaceSubject::IsSupported()
|
||||
{
|
||||
return OculusXRMovement::IsFaceTrackingSupported();
|
||||
}
|
||||
template <>
|
||||
bool FBodySubject::IsSupported()
|
||||
{
|
||||
return OculusXRMovement::IsBodyTrackingSupported();
|
||||
}
|
||||
template <>
|
||||
void FEyeSubject::UpdateFrame(FLiveLinkAnimationFrameData& FrameData)
|
||||
{
|
||||
bLastFrameIsValid = OculusXRMovement::GetEyeGazesState(LastState, 1.f)
|
||||
&& (LastState.EyeGazes[0].bIsValid || LastState.EyeGazes[1].bIsValid);
|
||||
if (bLastFrameIsValid)
|
||||
{
|
||||
constexpr auto FieldsCount = static_cast<uint8>(EOculusXREye::COUNT);
|
||||
FrameData.Transforms.Reserve(FieldsCount);
|
||||
for (uint8 i = 0u; i < FieldsCount; ++i)
|
||||
{
|
||||
const auto& EyeGaze = LastState.EyeGazes[i];
|
||||
FrameData.Transforms.Emplace(EyeGaze.Orientation, EyeGaze.Position);
|
||||
}
|
||||
FrameData.WorldTime = FPlatformTime::Seconds();
|
||||
}
|
||||
}
|
||||
template <>
|
||||
void FFaceSubject::UpdateFrame(FLiveLinkBaseFrameData& FrameData)
|
||||
{
|
||||
bLastFrameIsValid = OculusXRMovement::GetFaceState(LastState) && (LastState.bIsValid);
|
||||
if (bLastFrameIsValid)
|
||||
{
|
||||
constexpr auto FieldsCount = static_cast<uint8>(EOculusXRFaceExpression::COUNT);
|
||||
FrameData.PropertyValues.Reserve(FieldsCount);
|
||||
for (uint8 i = 0u; i < FieldsCount; ++i)
|
||||
{
|
||||
FrameData.PropertyValues.Emplace(LastState.ExpressionWeights[i]);
|
||||
}
|
||||
FrameData.WorldTime = FPlatformTime::Seconds();
|
||||
}
|
||||
}
|
||||
template <>
|
||||
void FBodySubject::UpdateFrame(FLiveLinkAnimationFrameData& FrameData)
|
||||
{
|
||||
bLastFrameIsValid = OculusXRMovement::GetBodyState(LastState, 1.f) && (LastState.IsActive) && (LastState.SkeletonChangedCount > 0);
|
||||
if (bLastFrameIsValid)
|
||||
{
|
||||
constexpr auto FieldsCount = static_cast<uint8>(EOculusXRBoneID::COUNT);
|
||||
FrameData.Transforms.Reserve(FieldsCount);
|
||||
for (uint8 i = 0u; i < FieldsCount; ++i)
|
||||
{
|
||||
const auto& Joint = LastState.Joints[i];
|
||||
FrameData.Transforms.Emplace(Joint.Orientation, Joint.Position);
|
||||
}
|
||||
FrameData.WorldTime = FPlatformTime::Seconds();
|
||||
}
|
||||
}
|
||||
|
||||
LiveLinkSource::LiveLinkSource()
|
||||
: bAnySupported(FEyeSubject::IsSupported() || FFaceSubject::IsSupported() || FBodySubject::IsSupported())
|
||||
{
|
||||
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FMovementSDKLiveLinkCreated>();
|
||||
}
|
||||
|
||||
void LiveLinkSource::ReceiveClient(ILiveLinkClient* InClient, FGuid InSourceGuid)
|
||||
{
|
||||
Client = InClient;
|
||||
SourceGuid = InSourceGuid;
|
||||
|
||||
InitializeMovementSubjects();
|
||||
UpdateMovementSubjects();
|
||||
}
|
||||
|
||||
bool LiveLinkSource::IsSourceStillValid() const
|
||||
{
|
||||
return Client != nullptr;
|
||||
}
|
||||
|
||||
bool LiveLinkSource::RequestSourceShutdown()
|
||||
{
|
||||
Client = nullptr;
|
||||
SourceGuid.Invalidate();
|
||||
|
||||
if (!(
|
||||
Body.Stop() && Face.Stop() && Eye.Stop()))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Error, TEXT("At least one of the trackers cannot stop."));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FText LiveLinkSource::GetSourceType() const
|
||||
{
|
||||
return LOCTEXT("MetaOculusXRMovementLiveLinkSourceType", "MetaXR MovementSDK");
|
||||
}
|
||||
|
||||
FText LiveLinkSource::GetSourceMachineName() const
|
||||
{
|
||||
if (IHeadMountedDisplayModule::IsAvailable())
|
||||
{
|
||||
const FString DeviceName = IHeadMountedDisplayModule::Get().GetDeviceSystemName();
|
||||
return FText::FromString(DeviceName);
|
||||
}
|
||||
return LOCTEXT("MetaOculusXRMovementLiveLinkMachineName", "MetaXR Device");
|
||||
}
|
||||
|
||||
FText LiveLinkSource::GetSourceStatus() const
|
||||
{
|
||||
if (bAnySupported)
|
||||
{
|
||||
return LOCTEXT("MetaOculusXRMovementLiveLinkStatusSupported", "Active");
|
||||
}
|
||||
return LOCTEXT("MetaOculusXRMovementLiveLinkStatusNotSupported", "Not Supported");
|
||||
}
|
||||
|
||||
void LiveLinkSource::Tick(float DeltaTime)
|
||||
{
|
||||
UpdateMovementSubjects();
|
||||
}
|
||||
|
||||
template <typename SubjectT>
|
||||
LiveLinkSource::ESubjectInitializationResult LiveLinkSource::InitializeMovementSubject(TOptional<FLiveLinkSubjectKey>& Key, SubjectT& Subject)
|
||||
{
|
||||
ESubjectInitializationResult FinalState;
|
||||
if (Key)
|
||||
{
|
||||
if (Key->Source.IsValid())
|
||||
{ // If the key was already in use. Remove it.
|
||||
Client->RemoveSubject_AnyThread(*Key);
|
||||
}
|
||||
Key.Reset();
|
||||
}
|
||||
if (Subject.IsSupported())
|
||||
{
|
||||
Key = FLiveLinkSubjectKey(SourceGuid, Subject.Name);
|
||||
FinalState = ESubjectInitializationResult::Started;
|
||||
if (!Subject.Start())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Error, TEXT("Tracker for LiveLink subject %s cannot start."), *Subject.Name.ToString());
|
||||
FinalState = ESubjectInitializationResult::StartFailed;
|
||||
}
|
||||
using Role = typename std::remove_reference_t<decltype(Subject)>::Role;
|
||||
Client->PushSubjectStaticData_AnyThread(*Key, Role::StaticClass(), Subject.StaticData());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Log, TEXT("LiveLink subject %s is not supported."), *Subject.Name.ToString());
|
||||
FinalState = ESubjectInitializationResult::NotSupported;
|
||||
}
|
||||
return FinalState;
|
||||
}
|
||||
|
||||
void LiveLinkSource::InitializeMovementSubjects()
|
||||
{
|
||||
check(IsInGameThread());
|
||||
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FMovementSDKLiveLinkInit> LiveLinkInit;
|
||||
|
||||
const auto EyeInit = InitializeMovementSubject(KeyEye, Eye);
|
||||
const auto FaceInit = InitializeMovementSubject(KeyFace, Face);
|
||||
const auto BodyInit = InitializeMovementSubject(KeyBody, Body);
|
||||
|
||||
LiveLinkInit.AddAnnotation(StringCast<ANSICHAR>(*Eye.Name.ToString()).Get(), ResultToText[static_cast<int>(EyeInit)])
|
||||
.AddAnnotation(StringCast<ANSICHAR>(*Face.Name.ToString()).Get(), ResultToText[static_cast<int>(FaceInit)])
|
||||
.AddAnnotation(StringCast<ANSICHAR>(*Body.Name.ToString()).Get(), ResultToText[static_cast<int>(BodyInit)])
|
||||
;
|
||||
}
|
||||
|
||||
template <typename SubjectT>
|
||||
void LiveLinkSource::UpdateMovementSubject(const TOptional<FLiveLinkSubjectKey>& Key, SubjectT& Subject)
|
||||
{
|
||||
if (Key)
|
||||
{
|
||||
const bool bPreviousFrameValid = Subject.IsLastFrameValid();
|
||||
auto FrameData = Subject.FrameData();
|
||||
const bool bFrameValid = Subject.IsLastFrameValid();
|
||||
if (bPreviousFrameValid != bFrameValid)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Log, TEXT("LiveLink subject %s became %s."), *Subject.Name.ToString(), bFrameValid ? TEXT("valid") : TEXT("invalid"));
|
||||
}
|
||||
if (bFrameValid)
|
||||
{
|
||||
Client->PushSubjectFrameData_AnyThread(*Key, MoveTemp(FrameData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LiveLinkSource::UpdateMovementSubjects()
|
||||
{
|
||||
check(IsInGameThread());
|
||||
if (IsSourceStillValid())
|
||||
{
|
||||
UpdateMovementSubject(KeyEye, Eye);
|
||||
UpdateMovementSubject(KeyFace, Face);
|
||||
UpdateMovementSubject(KeyBody, Body);
|
||||
}
|
||||
}
|
||||
} // namespace MetaXRMovement
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ILiveLinkSource.h"
|
||||
#include "LiveLinkTypes.h"
|
||||
#include "Roles/LiveLinkAnimationRole.h"
|
||||
#include "Roles/LiveLinkAnimationTypes.h"
|
||||
#include "Roles/LiveLinkBasicRole.h"
|
||||
#include "Tickable.h"
|
||||
|
||||
#include "OculusXRMovementTypes.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "MetaOculusXRMovement"
|
||||
|
||||
namespace MetaXRMovement
|
||||
{
|
||||
|
||||
template <typename MetaXRState, typename RoleTypeStaticData, typename RoleTypeFrameData, typename RoleT>
|
||||
class TSubject
|
||||
{
|
||||
public:
|
||||
explicit TSubject();
|
||||
using Role = RoleT;
|
||||
|
||||
const FLiveLinkSubjectName Name;
|
||||
|
||||
FLiveLinkStaticDataStruct StaticData() const;
|
||||
FLiveLinkFrameDataStruct FrameData();
|
||||
bool IsLastFrameValid() const { return bLastFrameIsValid; };
|
||||
bool Start();
|
||||
bool Stop();
|
||||
static bool IsSupported();
|
||||
|
||||
private:
|
||||
bool bLastFrameIsValid;
|
||||
bool bStarted;
|
||||
MetaXRState LastState;
|
||||
|
||||
void InitializeRoleStaticData(RoleTypeStaticData& StaticData) const;
|
||||
void UpdateFrame(RoleTypeFrameData& FrameData);
|
||||
};
|
||||
|
||||
using FEyeSubject = TSubject<FOculusXREyeGazesState, FLiveLinkSkeletonStaticData, FLiveLinkAnimationFrameData, ULiveLinkAnimationRole>;
|
||||
using FFaceSubject = TSubject<FOculusXRFaceState, FLiveLinkBaseStaticData, FLiveLinkBaseFrameData, ULiveLinkBasicRole>;
|
||||
using FBodySubject = TSubject<FOculusXRBodyState, FLiveLinkSkeletonStaticData, FLiveLinkAnimationFrameData, ULiveLinkAnimationRole>;
|
||||
|
||||
class LiveLinkSource : public ILiveLinkSource, public FTickableGameObject
|
||||
{
|
||||
public:
|
||||
LiveLinkSource();
|
||||
virtual ~LiveLinkSource() override = default;
|
||||
|
||||
// ILiveLinkSource implementation
|
||||
|
||||
virtual void ReceiveClient(ILiveLinkClient* InClient, FGuid InSourceGuid) override;
|
||||
virtual bool IsSourceStillValid() const override;
|
||||
virtual bool RequestSourceShutdown() override;
|
||||
virtual FText GetSourceType() const override;
|
||||
virtual FText GetSourceMachineName() const override;
|
||||
virtual FText GetSourceStatus() const override;
|
||||
|
||||
// FTickableGameObject implementation
|
||||
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
virtual bool IsTickable() const override { return bAnySupported && Client; };
|
||||
virtual TStatId GetStatId() const override
|
||||
{
|
||||
RETURN_QUICK_DECLARE_CYCLE_STAT(FOculusXRMovementLiveLink, STATGROUP_Tickables);
|
||||
}
|
||||
virtual bool IsTickableInEditor() const override { return true; }
|
||||
virtual bool IsTickableWhenPaused() const override { return true; }
|
||||
|
||||
private:
|
||||
enum class ESubjectInitializationResult
|
||||
{
|
||||
Started = 0,
|
||||
StartFailed = 1,
|
||||
NotSupported = 2
|
||||
};
|
||||
|
||||
static constexpr const char* ResultToText[]{ "started", "start_failed", "not_supported" };
|
||||
|
||||
template <typename SubjectT>
|
||||
ESubjectInitializationResult InitializeMovementSubject(TOptional<FLiveLinkSubjectKey>& Key, SubjectT& Subject);
|
||||
void InitializeMovementSubjects();
|
||||
template <typename SubjectT>
|
||||
void UpdateMovementSubject(const TOptional<FLiveLinkSubjectKey>& Key, SubjectT& Subject);
|
||||
void UpdateMovementSubjects();
|
||||
|
||||
// LiveLink Data
|
||||
// The local client to push data updates to
|
||||
ILiveLinkClient* Client{ nullptr };
|
||||
// Our identifier in LiveLink
|
||||
FGuid SourceGuid;
|
||||
|
||||
// Whenever any of the trackers is supported.
|
||||
const bool bAnySupported;
|
||||
|
||||
// This subject's keys. Initialized only if a tracker is supported.
|
||||
TOptional<FLiveLinkSubjectKey> KeyEye;
|
||||
TOptional<FLiveLinkSubjectKey> KeyFace;
|
||||
TOptional<FLiveLinkSubjectKey> KeyBody;
|
||||
|
||||
// Subjects
|
||||
FEyeSubject Eye;
|
||||
FFaceSubject Face;
|
||||
FBodySubject Body;
|
||||
};
|
||||
} // namespace MetaXRMovement
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOculusXRMovement, Log, All);
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRMovementModule.h"
|
||||
#include "OculusXRHMDModule.h"
|
||||
#include "OculusXRMovementLog.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMovement"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOculusXRMovement);
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRMovementModule
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
FOculusXRMovementModule::FOculusXRMovementModule()
|
||||
{
|
||||
}
|
||||
|
||||
void FOculusXRMovementModule::StartupModule()
|
||||
{
|
||||
}
|
||||
|
||||
void FOculusXRMovementModule::ShutdownModule()
|
||||
{
|
||||
}
|
||||
|
||||
TSharedPtr<ILiveLinkSource> FOculusXRMovementModule::GetLiveLinkSource()
|
||||
{
|
||||
if (!MovementSource.IsValid())
|
||||
{
|
||||
AddLiveLinkSource();
|
||||
}
|
||||
return MovementSource;
|
||||
}
|
||||
|
||||
bool FOculusXRMovementModule::IsLiveLinkSourceValid() const
|
||||
{
|
||||
return MovementSource.IsValid();
|
||||
}
|
||||
|
||||
void FOculusXRMovementModule::AddLiveLinkSource()
|
||||
{
|
||||
if (!MovementSource.IsValid())
|
||||
{
|
||||
MovementSource = MakeShared<MetaXRMovement::LiveLinkSource>();
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRMovementModule::RemoveLiveLinkSource()
|
||||
{
|
||||
MovementSource.Reset();
|
||||
}
|
||||
|
||||
IMPLEMENT_MODULE(FOculusXRMovementModule, OculusXRMovement)
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "ILiveLinkSource.h"
|
||||
|
||||
#include "IOculusXRMovementModule.h"
|
||||
#include "OculusXRMovement.h"
|
||||
#include "OculusXRMovementLiveLink.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMovement"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRMovementModule
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
class FOculusXRMovementModule : public IOculusXRMovementModule
|
||||
{
|
||||
public:
|
||||
FOculusXRMovementModule();
|
||||
|
||||
static inline FOculusXRMovementModule& Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<FOculusXRMovementModule>("OculusXRMovement");
|
||||
}
|
||||
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
/* Live link */
|
||||
virtual TSharedPtr<ILiveLinkSource> GetLiveLinkSource() override;
|
||||
virtual bool IsLiveLinkSourceValid() const override;
|
||||
virtual void AddLiveLinkSource() override;
|
||||
virtual void RemoveLiveLinkSource() override;
|
||||
|
||||
private:
|
||||
TSharedPtr<MetaXRMovement::LiveLinkSource> MovementSource{ nullptr };
|
||||
};
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRMovementTypes.h"
|
||||
#include "OculusXRHMDPrivate.h"
|
||||
#include "OculusXRHMD.h"
|
||||
|
||||
FOculusXRBodyJoint::FOculusXRBodyJoint()
|
||||
: LocationFlags(0)
|
||||
, bIsValid(false)
|
||||
, Orientation(FRotator::ZeroRotator)
|
||||
, Position(FVector::ZeroVector)
|
||||
{
|
||||
}
|
||||
|
||||
FOculusXRBodyState::FOculusXRBodyState()
|
||||
: IsActive(false)
|
||||
, Confidence(0)
|
||||
, SkeletonChangedCount(0)
|
||||
, Time(0.f)
|
||||
{
|
||||
Joints.SetNum(static_cast<int32>(EOculusXRBoneID::COUNT));
|
||||
}
|
||||
|
||||
|
||||
FOculusXRFaceState::FOculusXRFaceState()
|
||||
: bIsValid(false)
|
||||
, bIsEyeFollowingBlendshapesValid(false)
|
||||
, Time(0.f)
|
||||
{
|
||||
ExpressionWeights.SetNum(static_cast<int32>(EOculusXRFaceExpression::COUNT));
|
||||
ExpressionWeightConfidences.SetNum(static_cast<int32>(EOculusXRFaceConfidence::COUNT));
|
||||
}
|
||||
|
||||
FOculusXRFaceExpressionModifier::FOculusXRFaceExpressionModifier()
|
||||
: MinValue(0.f)
|
||||
, MaxValue(1.f)
|
||||
, Multiplier(1.f)
|
||||
{
|
||||
}
|
||||
|
||||
FOculusXREyeGazeState::FOculusXREyeGazeState()
|
||||
: Orientation(FRotator::ZeroRotator)
|
||||
, Position(FVector::ZeroVector)
|
||||
, Confidence(0.f)
|
||||
, bIsValid(false)
|
||||
{
|
||||
}
|
||||
|
||||
FOculusXREyeGazesState::FOculusXREyeGazesState()
|
||||
: Time(0.f)
|
||||
{
|
||||
EyeGazes.SetNum(static_cast<int32>(EOculusXREye::COUNT));
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRTelemetry.h"
|
||||
|
||||
namespace OculusXRTelemetry::Events
|
||||
{
|
||||
using FMovementSDKLiveLinkCreated = TMarker<191961034>;
|
||||
using FMovementSDKLiveLinkInit = TMarker<191970472>;
|
||||
using FMovementSDKBodyStart = TMarker<191958900>;
|
||||
using FMovementSDKFaceStart = TMarker<191966310>;
|
||||
using FMovementSDKEyeStart = TMarker<191969182>;
|
||||
} // namespace OculusXRTelemetry::Events
|
||||
Reference in New Issue
Block a user