Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0878e23421
|
||
|
|
88094a0f05
|
||
|
|
94261e3832
|
||
|
|
c4df67c5dc
|
||
|
|
a6df3df532
|
||
|
|
534c093fc5
|
||
|
|
307d13fd7d
|
||
|
|
3056a42666
|
||
|
|
9a4088a300
|
||
|
|
edc0a18add
|
||
|
|
ac3489c2af
|
||
|
|
545c2fd5a3
|
||
|
|
b17d1d0823
|
||
|
|
871409a2d7
|
||
|
|
451e1b9a59
|
||
|
|
50043bee92
|
||
|
|
b91bfea3d9
|
||
|
|
ca9251f6f7
|
||
|
|
397f39a089
|
||
|
|
ad2ed303bd
|
||
|
|
5ddc5108f4
|
+34
-141
@@ -68,123 +68,40 @@ DefaultGraphicsPerformance=Maximum
|
||||
AppliedDefaultGraphicsPerformance=Maximum
|
||||
|
||||
[/Script/Engine.RendererSettings]
|
||||
r.Mobile.DisableVertexFog=True
|
||||
r.Mobile.AllowDitheredLODTransition=False
|
||||
r.Mobile.AllowSoftwareOcclusion=False
|
||||
r.Mobile.VirtualTextures=False
|
||||
r.DiscardUnusedQuality=False
|
||||
r.AllowOcclusionQueries=True
|
||||
r.MinScreenRadiusForLights=0.030000
|
||||
r.MinScreenRadiusForDepthPrepass=0.030000
|
||||
r.MinScreenRadiusForCSMDepth=0.010000
|
||||
r.PrecomputedVisibilityWarning=False
|
||||
r.TextureStreaming=True
|
||||
Compat.UseDXT5NormalMaps=False
|
||||
r.VirtualTextures=False
|
||||
r.VirtualTexturedLightmaps=False
|
||||
r.VT.TileSize=128
|
||||
r.VT.TileBorderSize=4
|
||||
r.vt.FeedbackFactor=16
|
||||
r.VT.EnableCompressZlib=True
|
||||
r.VT.EnableCompressCrunch=False
|
||||
r.ClearCoatNormal=False
|
||||
r.ReflectionCaptureResolution=128
|
||||
r.ReflectionEnvironmentLightmapMixBasedOnRoughness=True
|
||||
r.ForwardShading=True
|
||||
r.VertexFoggingForOpaque=True
|
||||
r.AllowStaticLighting=True
|
||||
r.NormalMapsForStaticLighting=False
|
||||
r.GenerateMeshDistanceFields=False
|
||||
r.DistanceFieldBuild.EightBit=False
|
||||
r.GenerateLandscapeGIData=False
|
||||
r.DistanceFieldBuild.Compress=False
|
||||
r.TessellationAdaptivePixelsPerTriangle=48.000000
|
||||
r.SeparateTranslucency=True
|
||||
r.TranslucentSortPolicy=0
|
||||
TranslucentSortAxis=(X=0.000000,Y=-1.000000,Z=0.000000)
|
||||
r.CustomDepth=1
|
||||
r.CustomDepthTemporalAAJitter=True
|
||||
r.PostProcessing.PropagateAlpha=2
|
||||
r.DefaultFeature.Bloom=False
|
||||
r.Nanite=0
|
||||
r.DefaultFeature.AmbientOcclusion=False
|
||||
r.DefaultFeature.AmbientOcclusionStaticFraction=True
|
||||
r.DefaultFeature.AutoExposure=False
|
||||
r.DefaultFeature.AutoExposure.Method=0
|
||||
r.DefaultFeature.AutoExposure.Bias=1.000000
|
||||
r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
|
||||
r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
|
||||
r.EyeAdaptation.EditorOnly=False
|
||||
r.DefaultFeature.MotionBlur=False
|
||||
r.DefaultFeature.LensFlare=False
|
||||
r.TemporalAA.Upsampling=False
|
||||
r.SSGI.Enable=False
|
||||
r.AntiAliasingMethod=3
|
||||
r.DefaultFeature.LightUnits=1
|
||||
r.DefaultBackBufferPixelFormat=4
|
||||
r.Shadow.UnbuiltPreviewInGame=True
|
||||
r.StencilForLODDither=False
|
||||
r.EarlyZPass=3
|
||||
r.EarlyZPassOnlyMaterialMasking=False
|
||||
r.DBuffer=True
|
||||
r.ClearSceneMethod=1
|
||||
r.VelocityOutputPass=0
|
||||
r.Velocity.EnableVertexDeformation=0
|
||||
r.SelectiveBasePassOutputs=False
|
||||
bDefaultParticleCutouts=False
|
||||
fx.GPUSimulationTextureSizeX=1024
|
||||
fx.GPUSimulationTextureSizeY=1024
|
||||
r.AllowGlobalClipPlane=False
|
||||
r.GBufferFormat=1
|
||||
r.MorphTarget.Mode=True
|
||||
r.GPUCrashDebugging=False
|
||||
r.DefaultFeature.AmbientOcclusionStaticFraction=False
|
||||
vr.InstancedStereo=True
|
||||
r.MobileHDR=False
|
||||
vr.MobileMultiView=True
|
||||
r.SeparateTranslucency=0
|
||||
r.HZBOcclusion=0
|
||||
r.EyeAdaptionQuality=0
|
||||
r.SSR.MaxRoughness=0
|
||||
r.rhicmdbypass=0
|
||||
r.TiledReflectionEnvironmentMinimumCount=10
|
||||
r.ForwardShading=True
|
||||
r.DefaultFeature.AntiAliasing=3
|
||||
r.AllowStaticLighting=True
|
||||
r.DefaultFeature.MotionBlur=False
|
||||
r.DefaultFeature.Bloom=True
|
||||
r.DefaultFeature.AutoExposure=False
|
||||
r.CustomDepth=3
|
||||
r.CustomDepthTemporalAAJitter=True
|
||||
r.EarlyZPass=3
|
||||
r.EarlyZPassMovable=True
|
||||
r.GBufferFormat=1
|
||||
r.Mobile.UseHWsRGBEncoding=True
|
||||
vr.RoundRobinOcclusion=False
|
||||
vr.ODSCapture=False
|
||||
r.MeshStreaming=False
|
||||
r.WireframeCullThreshold=5.000000
|
||||
r.RayTracing=False
|
||||
r.RayTracing.UseTextureLod=False
|
||||
r.SupportStationarySkylight=True
|
||||
r.SupportLowQualityLightmaps=True
|
||||
r.SupportPointLightWholeSceneShadows=True
|
||||
r.SupportAtmosphericFog=True
|
||||
r.SupportSkyAtmosphere=True
|
||||
r.SupportSkyAtmosphereAffectsHeightFog=False
|
||||
r.SkinCache.CompileShaders=False
|
||||
r.SkinCache.DefaultBehavior=1
|
||||
r.SkinCache.SceneMemoryLimitInMB=128.000000
|
||||
r.Mobile.EnableStaticAndCSMShadowReceivers=True
|
||||
r.Mobile.EnableMovableLightCSMShaderCulling=True
|
||||
r.Mobile.AllowDistanceFieldShadows=True
|
||||
r.Mobile.AllowMovableDirectionalLights=True
|
||||
r.MobileNumDynamicPointLights=4
|
||||
r.MobileDynamicPointLightsUseStaticBranch=True
|
||||
r.Mobile.EnableMovableSpotlights=False
|
||||
r.Mobile.EnableMovableSpotlightsShadow=False
|
||||
r.GPUSkin.Support16BitBoneIndex=False
|
||||
r.GPUSkin.Limit2BoneInfluences=False
|
||||
r.SupportDepthOnlyIndexBuffers=True
|
||||
r.SupportReversedIndexBuffers=True
|
||||
r.LightPropagationVolume=False
|
||||
r.Mobile.AmbientOcclusion=False
|
||||
r.GPUSkin.UnlimitedBoneInfluences=False
|
||||
r.GPUSkin.UnlimitedBoneInfluencesThreshold=8
|
||||
r.Mobile.PlanarReflectionMode=0
|
||||
bStreamSkeletalMeshLODs=(Default=False,PerPlatform=())
|
||||
bDiscardSkeletalMeshOptionalLODs=(Default=False,PerPlatform=())
|
||||
VisualizeCalibrationColorMaterialPath=None
|
||||
VisualizeCalibrationCustomMaterialPath=None
|
||||
VisualizeCalibrationGrayscaleMaterialPath=None
|
||||
r.MobileMSAA=4
|
||||
r.MobileHDR=False
|
||||
r.AntiAliasingMethod=3
|
||||
r.Mobile.AntiAliasing=3
|
||||
r.Mobile.FloatPrecisionMode=2
|
||||
r.OpenGL.ForceDXC=0
|
||||
r.DynamicGlobalIlluminationMethod=1
|
||||
r.ReflectionMethod=1
|
||||
r.Shadow.Virtual.Enable=0
|
||||
r.Lumen.TranslucencyReflections.FrontLayer.EnableForProject=False
|
||||
vr.VRS.HMDFixedFoveationDynamic=False
|
||||
vr.VRS.HMDFixedFoveationLevel=0
|
||||
vr.MobileMultiView=True
|
||||
r.Mobile.ShadingPath=0
|
||||
r.Lumen.Reflections.HardwareRayTracing.Translucent.Refraction.EnableForProject=False
|
||||
r.RayTracing=False
|
||||
r.Nanite.ProjectEnabled=False
|
||||
r.NormalMapsForStaticLighting=False
|
||||
|
||||
[/Script/NavigationSystem.NavigationSystemV1]
|
||||
bAutoCreateNavigationData=True
|
||||
@@ -404,19 +321,19 @@ ChaosSettings=(DefaultThreadingModel=SingleThread,DedicatedThreadTickMode=Variab
|
||||
SolverOptions=(Iterations=8,CollisionPairIterations=1,PushOutIterations=4,CollisionPushOutPairIterations=4,CollisionMarginFraction=0.050000,CollisionMarginMax=10.000000,CollisionCullDistance=3.000000,CollisionMaxPushOutVelocity=100000.000000,JointPairIterations=1,JointPushOutPairIterations=1,ClusterConnectionFactor=1.000000,ClusterUnionConnectionType=DelaunayTriangulation,bGenerateCollisionData=False,CollisionFilterSettings=(FilterEnabled=False,MinMass=0.000000,MinSpeed=0.000000,MinImpulse=0.000000),bGenerateBreakData=False,BreakingFilterSettings=(FilterEnabled=False,MinMass=0.000000,MinSpeed=0.000000,MinVolume=0.000000),bGenerateTrailingData=False,TrailingFilterSettings=(FilterEnabled=False,MinMass=0.000000,MinSpeed=0.000000,MinVolume=0.000000),bGenerateContactGraph=True)
|
||||
|
||||
[/Script/AndroidRuntimeSettings.AndroidRuntimeSettings]
|
||||
PackageName=com.senseglove.sgvretemplate
|
||||
PackageName=com.YourCompany.[PROJECT]
|
||||
StoreVersion=1
|
||||
StoreVersionOffsetArm64=0
|
||||
StoreVersionOffsetX8664=0
|
||||
ApplicationDisplayName=
|
||||
VersionDisplayName=1.0
|
||||
MinSDKVersion=29
|
||||
MinSDKVersion=32
|
||||
TargetSDKVersion=33
|
||||
InstallLocation=InternalOnly
|
||||
bEnableLint=False
|
||||
bPackageDataInsideApk=True
|
||||
bPackageDataInsideApk=False
|
||||
bCreateAllPlatformsInstall=False
|
||||
bDisableVerifyOBBOnStartUp=True
|
||||
bDisableVerifyOBBOnStartUp=False
|
||||
bForceSmallOBBFiles=False
|
||||
bAllowLargeOBBFiles=False
|
||||
bAllowPatchOBBFile=False
|
||||
@@ -548,27 +465,3 @@ DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
|
||||
[URL]
|
||||
GameName=SGVRETemplate
|
||||
|
||||
[/Script/ViveOpenXRRuntimeSettings.ViveOpenXRRuntimeSettings]
|
||||
bEnableFocus3Controller=False
|
||||
bEnableCosmosController=False
|
||||
bEnableSimultaneousInteraction=False
|
||||
bEnableHandInteraction=True
|
||||
bUseHTCHandInteraction=False
|
||||
bEnableWristTracker=True
|
||||
bEnableXrTracker=False
|
||||
bEnableXrTrackerInputs=False
|
||||
bEnableFacialTracking=False
|
||||
bEnablePassthrough=False
|
||||
PassthroughQualityScale=1.000000
|
||||
PassthroughRateType=Normal
|
||||
bEnableSharpening=False
|
||||
SharpeningLevel=0.000000
|
||||
SharpeningMode=NORMAL
|
||||
bEnableAnchor=False
|
||||
bEnablePlaneDetection=False
|
||||
bEnableSceneUnderstanding=False
|
||||
bEnableDisplayRefreshRate=False
|
||||
bEnableHTCEyeTracker=False
|
||||
bEnableHandTracking=False
|
||||
bEnableEyeTracking=False
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
|
||||
[/Script/SenseGloveSettings.SGSettings]
|
||||
TrackingSettings=(bFallbackToHandTrackingIfNoGloveDetected=False,GloveTrackingSettings=(GloveConnectivityCheckInterval=16.666666),HandTrackingSettings=(bUseMoreSpecificMotionSourceNames=False,bSupportLegacyControllerMotionSources=True),HMDTrackingSettings=(ViveHMDDetectionPriority=FocusVision_XRElite_Focus3),WristTrackingSettings=(OpenXRPositionalTrackingProvider=OpenXRViveWristTracker,TrackingHardware=ViveFocus3WristTracker,TrackingHardwareLocationOffsetLeftHand=(X=0.000000,Y=0.000000,Z=0.000000),TrackingHardwareLocationOffsetRightHand=(X=0.000000,Y=0.000000,Z=0.000000),TrackingHardwareRotationOffsetLeftHand=(Pitch=0.000000,Yaw=0.000000,Roll=0.000000),TrackingHardwareRotationOffsetRightHand=(Pitch=0.000000,Yaw=0.000000,Roll=0.000000),LeftHandMotionSource="LeftWristTracker",RightHandMotionSource="RightWristTracker",DebuggingSettings=(bDrawDebugWristTracker=False,DebugWristTrackerSettings=(Length=4.000000,XAxisColor=(B=0,G=0,R=255,A=255),YAxisColor=(B=0,G=255,R=0,A=255),ZAxisColor=(B=255,G=0,R=0,A=255),bPersistentLines=False,LifeTimeModifier=1.100000,DepthPriority=0,Thickness=0.400000))))
|
||||
TrackingSettings=(bFallbackToHandTrackingIfNoGloveDetected=False,GloveTrackingSettings=(GloveConnectivityCheckInterval=16.666666),HandTrackingSettings=(bUseMoreSpecificMotionSourceNames=False,bSupportLegacyControllerMotionSources=True),HMDTrackingSettings=(ViveHMDDetectionPriority=FocusVision_XRElite_Focus3),WristTrackingSettings=(OpenXRPositionalTrackingProvider=OpenXRViveTracker,TrackingHardware=ViveFocus3WristTracker,TrackingHardwareLocationOffsetLeftHand=(X=0.000000,Y=0.000000,Z=0.000000),TrackingHardwareLocationOffsetRightHand=(X=0.000000,Y=0.000000,Z=0.000000),TrackingHardwareRotationOffsetLeftHand=(Pitch=0.000000,Yaw=0.000000,Roll=0.000000),TrackingHardwareRotationOffsetRightHand=(Pitch=0.000000,Yaw=0.000000,Roll=0.000000),LeftHandMotionSource="LeftFoot",RightHandMotionSource="RightFoot",DebuggingSettings=(bDrawDebugWristTracker=False,DebugWristTrackerSettings=(Length=4.000000,XAxisColor=(B=0,G=0,R=255,A=255),YAxisColor=(B=0,G=255,R=0,A=255),ZAxisColor=(B=255,G=0,R=0,A=255),bPersistentLines=False,LifeTimeModifier=1.100000,DepthPriority=0,Thickness=0.400000))))
|
||||
|
||||
|
||||
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,386 +0,0 @@
|
||||
// Copyright (c) 2022 HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveCustomHandGesture.h"
|
||||
#include <cmath>
|
||||
#include "IXRTrackingSystem.h"
|
||||
#include "Math/Quat.h"
|
||||
#include "Math/UnrealMathUtility.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogViveCustomHandGesture);
|
||||
|
||||
// Sets default values
|
||||
AViveCustomHandGesture::AViveCustomHandGesture()
|
||||
{
|
||||
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
s_JointPositionsLeft.Init(FVector::ZeroVector, EWaveHandJointCount); // count of WVR_HandJoint
|
||||
s_JointPositionsRight.Init(FVector::ZeroVector, EWaveHandJointCount); // count of WVR_HandJoint
|
||||
s_JointRotationsLeft.Init(FQuat::Identity, EWaveHandJointCount); // count of WVR_HandJoint
|
||||
s_JointRotationsRight.Init(FQuat::Identity, EWaveHandJointCount); // count of WVR_HandJoint
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void AViveCustomHandGesture::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void AViveCustomHandGesture::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
FXRMotionControllerData ControllerDataLeft, ControllerDataRight;
|
||||
IXRTrackingSystem* TrackingSys = GEngine->XRSystem.Get();
|
||||
if (TrackingSys != nullptr) {
|
||||
TrackingSys->GetMotionControllerData(GetWorld(), EControllerHand::Left, ControllerDataLeft);
|
||||
TrackingSys->GetMotionControllerData(GetWorld(), EControllerHand::Right, ControllerDataRight);
|
||||
}
|
||||
|
||||
bool LeftIsHand = (ControllerDataLeft.DeviceVisualType == EXRVisualType::Hand);
|
||||
bool RightIsHand = (ControllerDataRight.DeviceVisualType == EXRVisualType::Hand);
|
||||
|
||||
validPoseLeft = ControllerDataLeft.bValid;
|
||||
if (LeftIsHand && validPoseLeft) {
|
||||
for (int32 idx = 0; idx < EWaveHandJointCount; idx++) {
|
||||
s_JointPositionsLeft[idx] = ControllerDataLeft.HandKeyPositions[idx];
|
||||
s_JointRotationsLeft[idx] = ControllerDataLeft.HandKeyRotations[idx];
|
||||
}
|
||||
}
|
||||
validPoseRight = ControllerDataRight.bValid;
|
||||
if (RightIsHand && validPoseRight) {
|
||||
for (int32 idx = 0; idx < EWaveHandJointCount; idx++) {
|
||||
s_JointPositionsRight[idx] = ControllerDataRight.HandKeyPositions[idx];
|
||||
s_JointRotationsRight[idx] = ControllerDataRight.HandKeyRotations[idx];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Updates all fingers' states.
|
||||
UpdateFingerState();
|
||||
|
||||
/// Checks left gestures.
|
||||
bool matchLeft = false;
|
||||
for (int i = 0; i < LeftGestures.Num(); i++)
|
||||
{
|
||||
if (MatchGestureSingle(LeftGestures[i].Setting, true))
|
||||
{
|
||||
if (!m_LeftHandGesture.Equals(LeftGestures[i].Name))
|
||||
{
|
||||
m_LeftHandGesture = LeftGestures[i].Name;
|
||||
UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() broadcast Left custome gesture match %s."), *m_LeftHandGesture);
|
||||
UViveCustomGestureComponent::OnCustomHandGestureNative_Left.Broadcast(m_LeftHandGesture);
|
||||
}
|
||||
|
||||
matchLeft = true;
|
||||
break; // Leaves if matched gesture.
|
||||
}
|
||||
}
|
||||
if (!matchLeft && !m_LeftHandGesture.Equals(kUnknownGesture))
|
||||
{
|
||||
m_LeftHandGesture = kUnknownGesture;
|
||||
UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() broadcast Left custome gesture %s."), *m_LeftHandGesture);
|
||||
UViveCustomGestureComponent::OnCustomHandGestureNative_Left.Broadcast(m_LeftHandGesture);
|
||||
}
|
||||
|
||||
/// Checks right gestures.
|
||||
bool matchRight = false;
|
||||
for (int i = 0; i < RightGestures.Num(); i++)
|
||||
{
|
||||
if (MatchGestureSingle(RightGestures[i].Setting, false))
|
||||
{
|
||||
if (!m_RightHandGesture.Equals(RightGestures[i].Name))
|
||||
{
|
||||
m_RightHandGesture = RightGestures[i].Name;
|
||||
UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() broadcast Right custome gesture match %s."), *m_RightHandGesture);
|
||||
UViveCustomGestureComponent::OnCustomHandGestureNative_Right.Broadcast(m_RightHandGesture);
|
||||
}
|
||||
|
||||
matchRight = true;
|
||||
break; // Leaves if matched gesture.
|
||||
}
|
||||
}
|
||||
if (!matchRight && !m_RightHandGesture.Equals(kUnknownGesture))
|
||||
{
|
||||
m_RightHandGesture = kUnknownGesture;
|
||||
UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() broadcast Right custome gesture %s."), *m_RightHandGesture);
|
||||
UViveCustomGestureComponent::OnCustomHandGestureNative_Right.Broadcast(m_RightHandGesture);
|
||||
}
|
||||
|
||||
bool matchDual = false;
|
||||
for (int i = 0; i < DualHandGestures.Num(); i++)
|
||||
{
|
||||
if (MatchGestureDual(DualHandGestures[i].Setting))
|
||||
{
|
||||
if (!m_DualHandGesture.Equals(DualHandGestures[i].Name))
|
||||
{
|
||||
m_DualHandGesture = DualHandGestures[i].Name;
|
||||
UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() broadcast Dual Hand custome gesture %s."), *m_DualHandGesture);
|
||||
UViveCustomGestureComponent::OnCustomHandGestureNative_Dual.Broadcast(m_DualHandGesture);
|
||||
}
|
||||
|
||||
matchDual = true;
|
||||
break; // Leaves if matched gesture.
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchDual && !m_DualHandGesture.Equals(kUnknownGesture))
|
||||
{
|
||||
m_DualHandGesture = kUnknownGesture;
|
||||
UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() broadcast Dual Hand custome gesture %s."), *m_DualHandGesture);
|
||||
UViveCustomGestureComponent::OnCustomHandGestureNative_Dual.Broadcast(m_DualHandGesture);
|
||||
}
|
||||
}
|
||||
|
||||
void AViveCustomHandGesture::UpdateFingerState()
|
||||
{
|
||||
if (validPoseLeft)
|
||||
{
|
||||
m_ThumbStateLeft = GetThumbState(
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Thumb_Joint1],
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Thumb_Joint2],
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Thumb_Tip],
|
||||
true
|
||||
);
|
||||
|
||||
m_IndexStateLeft = GetFingerState(
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Index_Joint1],
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Index_Joint2],
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Index_Tip],
|
||||
true
|
||||
);
|
||||
|
||||
m_MiddleStateLeft = GetFingerState(
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Middle_Joint1],
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Middle_Joint2],
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Middle_Tip],
|
||||
true
|
||||
);
|
||||
|
||||
m_RingStateLeft = GetFingerState(
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Ring_Joint1],
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Ring_Joint2],
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Ring_Tip],
|
||||
true
|
||||
);
|
||||
|
||||
m_PinkyStateLeft = GetFingerState(
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Pinky_Joint1],
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Pinky_Joint2],
|
||||
s_JointPositionsLeft[(uint8)EWaveHandJoint::Pinky_Tip],
|
||||
true
|
||||
);
|
||||
}
|
||||
if (validPoseRight)
|
||||
{
|
||||
m_ThumbStateRight = GetThumbState(
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Thumb_Joint1],
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Thumb_Joint2],
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Thumb_Tip],
|
||||
false
|
||||
);
|
||||
|
||||
//UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() Call each GetFingerState."));
|
||||
|
||||
m_IndexStateRight = GetFingerState(
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Index_Joint1],
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Index_Joint2],
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Index_Tip],
|
||||
false
|
||||
);
|
||||
|
||||
m_MiddleStateRight = GetFingerState(
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Middle_Joint1],
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Middle_Joint2],
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Middle_Tip],
|
||||
false
|
||||
);
|
||||
|
||||
m_RingStateRight = GetFingerState(
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Ring_Joint1],
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Ring_Joint2],
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Ring_Tip],
|
||||
false
|
||||
);
|
||||
|
||||
m_PinkyStateRight = GetFingerState(
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Pinky_Joint1],
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Pinky_Joint2],
|
||||
s_JointPositionsRight[(uint8)EWaveHandJoint::Pinky_Tip],
|
||||
false
|
||||
);
|
||||
|
||||
//UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() Right finger status: thumb [%d], index [%d], middle [%d], ring [%d], pinky [%d]."), m_ThumbStateRight, m_IndexStateRight, m_MiddleStateRight, m_RingStateRight, m_PinkyStateRight);
|
||||
}
|
||||
}
|
||||
EWaveThumbState AViveCustomHandGesture::GetThumbState(FVector root, FVector node1, FVector top, bool isLeft)
|
||||
{
|
||||
if (isLeft && !validPoseLeft) { return EWaveThumbState::None; }
|
||||
if (!isLeft && !validPoseRight) { return EWaveThumbState::None; }
|
||||
|
||||
return WaveHandHelper::GetThumbState(root, node1, top);
|
||||
}
|
||||
EWaveFingerState AViveCustomHandGesture::GetFingerState(FVector root, FVector node1, FVector top, bool isLeft)
|
||||
{
|
||||
if (isLeft && !validPoseLeft) { return EWaveFingerState::None; }
|
||||
if (!isLeft && !validPoseRight) { return EWaveFingerState::None; }
|
||||
|
||||
return WaveHandHelper::GetFingerState(root, node1, top);
|
||||
}
|
||||
bool AViveCustomHandGesture::MatchThumbState(FThumbState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AViveCustomHandGesture::MatchDistanceSingle(EWaveHandJoint node1, EWaveHandJoint node2, EWaveJointDistance distance, bool isLeft)
|
||||
{
|
||||
if (isLeft && !validPoseLeft) { return false; }
|
||||
if (!isLeft && !validPoseRight) { return false; }
|
||||
|
||||
float node_dist = isLeft ?
|
||||
FVector::Distance(s_JointPositionsLeft[(uint8)node1], s_JointPositionsLeft[(uint8)node2]) :
|
||||
FVector::Distance(s_JointPositionsRight[(uint8)node1], s_JointPositionsRight[(uint8)node2]);
|
||||
|
||||
if (distance == EWaveJointDistance::Near) { return (node_dist < 2.5f); }
|
||||
return (node_dist > 5);
|
||||
}
|
||||
bool AViveCustomHandGesture::MatchDistanceDual(EWaveHandJoint leftNode, EWaveHandJoint rightNode, EWaveJointDistance distance)
|
||||
{
|
||||
if (!validPoseLeft || !validPoseRight) { return false; }
|
||||
|
||||
float node_dist = FVector::Distance(s_JointPositionsLeft[(uint8)leftNode], s_JointPositionsRight[(uint8)rightNode]);
|
||||
|
||||
if (distance == EWaveJointDistance::Near) { return (node_dist < 10); }
|
||||
return (node_dist > 20);
|
||||
}
|
||||
|
||||
bool AViveCustomHandGesture::MatchRotationSingle(FRotator rotateCondition, bool isLeft)
|
||||
{
|
||||
if (isLeft && !validPoseLeft) { return false; }
|
||||
if (!isLeft && !validPoseRight) { return false; }
|
||||
|
||||
|
||||
float Offset = std::ceil(0.2 * 180 / PI);
|
||||
|
||||
float minX = (rotateCondition.Roll > 0) ? (rotateCondition.Roll - Offset) : (rotateCondition.Roll - Offset);
|
||||
float maxX = (rotateCondition.Roll > 0) ? (rotateCondition.Roll + Offset) : (rotateCondition.Roll + Offset);
|
||||
|
||||
float minY = rotateCondition.Pitch - Offset;
|
||||
float maxY = rotateCondition.Pitch + Offset;
|
||||
|
||||
float minZ = rotateCondition.Yaw - Offset;
|
||||
float maxZ = rotateCondition.Yaw + Offset;
|
||||
|
||||
|
||||
EulerAngles eulerAngles;
|
||||
bool xRotate = true, yRotate = true, zRotate = true;
|
||||
eulerAngles = isLeft ?
|
||||
ToEulerAnglesDegree(s_JointRotationsLeft[(uint8)EWaveHandJoint::Palm]) :
|
||||
ToEulerAnglesDegree(s_JointRotationsRight[(uint8)EWaveHandJoint::Palm]);
|
||||
//if (isLeft) {
|
||||
//UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() Left gesture Palm Angle(r,p,y) %f, %f, %f."), eulerAngles.roll, eulerAngles.pitch, eulerAngles.yaw);
|
||||
//} else
|
||||
//UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() Right gesture Palm Angle(r,p,y) %f, %f, %f."), eulerAngles.roll, eulerAngles.pitch, eulerAngles.yaw);
|
||||
//UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() Condition(r,p,y) %f, %f, %f, %f, %f, %f. offset %f"), minX, maxX, minY, maxY, minZ, maxZ, Offset);
|
||||
|
||||
// Skip check when condition valuse is 0
|
||||
if (rotateCondition.Roll != 0)
|
||||
{
|
||||
if (eulerAngles.roll < minX || eulerAngles.roll > maxX) { return false; }
|
||||
}
|
||||
if (rotateCondition.Pitch != 0)
|
||||
{
|
||||
if (eulerAngles.pitch < minY || eulerAngles.pitch > maxY) { return false; }
|
||||
}
|
||||
if (rotateCondition.Yaw != 0)
|
||||
{
|
||||
if (std::abs(eulerAngles.yaw) < minZ || std::abs(eulerAngles.yaw) > maxZ) { return false; }
|
||||
}
|
||||
|
||||
//UE_LOG(LogViveCustomHandGesture, Log, TEXT("Tick() MatchRotationSingle result %d %d %d."), xRotate, yRotate, zRotate);
|
||||
return (xRotate && yRotate && zRotate);
|
||||
}
|
||||
|
||||
bool AViveCustomHandGesture::MatchGestureSingle(FSingleHandSetting setting, bool isLeft)
|
||||
{
|
||||
if (isLeft)
|
||||
{
|
||||
if (!validPoseLeft) { return false; }
|
||||
|
||||
if (!WaveHandHelper::MatchThumbState(setting.Thumb, m_ThumbStateLeft)) { return false; }
|
||||
if (!WaveHandHelper::MatchFingerState(setting.Index, m_IndexStateLeft)) { return false; }
|
||||
if (!WaveHandHelper::MatchFingerState(setting.Middle, m_MiddleStateLeft)) { return false; }
|
||||
if (!WaveHandHelper::MatchFingerState(setting.Ring, m_RingStateLeft)) { return false; }
|
||||
if (!WaveHandHelper::MatchFingerState(setting.Pinky, m_PinkyStateLeft)) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!validPoseRight) { return false; }
|
||||
|
||||
if (!WaveHandHelper::MatchThumbState(setting.Thumb, m_ThumbStateRight)) { return false; }
|
||||
if (!WaveHandHelper::MatchFingerState(setting.Index, m_IndexStateRight)) { return false; }
|
||||
if (!WaveHandHelper::MatchFingerState(setting.Middle, m_MiddleStateRight)) { return false; }
|
||||
if (!WaveHandHelper::MatchFingerState(setting.Ring, m_RingStateRight)) { return false; }
|
||||
if (!WaveHandHelper::MatchFingerState(setting.Pinky, m_PinkyStateRight)) { return false; }
|
||||
}
|
||||
|
||||
/// Checks all distance conditions.
|
||||
for (int i = 0; i < setting.SingleHandNodeDistances.Num(); i++)
|
||||
{
|
||||
if (!MatchDistanceSingle(setting.SingleHandNodeDistances[i].Node1, setting.SingleHandNodeDistances[i].Node2, setting.SingleHandNodeDistances[i].Distance, isLeft))
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Check palm Rotation
|
||||
if (!MatchRotationSingle(setting.RotationCondition, isLeft))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool AViveCustomHandGesture::MatchGestureDual(FDualHandSetting setting)
|
||||
{
|
||||
if (!validPoseLeft || !validPoseRight) { return false; }
|
||||
|
||||
if (!MatchGestureSingle(setting.LeftHand, true)) { return false; }
|
||||
if (!MatchGestureSingle(setting.RightHand, false)) { return false; }
|
||||
|
||||
/// Checks all distance conditions.
|
||||
for (int i = 0; i < setting.DualHandNodeDistances.Num(); i++)
|
||||
{
|
||||
if (!MatchDistanceDual(setting.DualHandNodeDistances[i].LeftNode, setting.DualHandNodeDistances[i].RightNode, setting.DualHandNodeDistances[i].Distance))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EulerAngles AViveCustomHandGesture::ToEulerAnglesDegree(FQuat q) {
|
||||
EulerAngles angles;
|
||||
//FRotator angles;
|
||||
|
||||
// roll (x-axis rotation)
|
||||
double sinr_cosp = 2 * (q.W * q.X + q.Y * q.Z);
|
||||
double cosr_cosp = 1 - 2 * (q.X * q.X + q.Y * q.Y);
|
||||
angles.roll = FMath::Atan2(sinr_cosp, cosr_cosp);
|
||||
|
||||
// pitch (y-axis rotation)
|
||||
double sinp = 2 * (q.W * q.Y - q.Z * q.X);
|
||||
if (std::abs(sinp) >= 1)
|
||||
angles.pitch = std::copysign(PI / 2, sinp); // use 90 degrees if out of range
|
||||
else
|
||||
angles.pitch = FMath::Asin(sinp);
|
||||
|
||||
// yaw (z-axis rotation)
|
||||
double siny_cosp = 2 * (q.W * q.Z + q.X * q.Y);
|
||||
double cosy_cosp = 1 - 2 * (q.Y * q.Y + q.Z * q.Z);
|
||||
angles.yaw = FMath::Atan2(siny_cosp, cosy_cosp);
|
||||
|
||||
angles.roll = round(angles.roll * 180 / PI);
|
||||
angles.pitch = round(angles.pitch * 180 / PI);
|
||||
angles.yaw = round(angles.yaw * 180 / PI);
|
||||
return angles;
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveCustomHandGestureComponent.h"
|
||||
#include "ViveCustomHandGesture.h"
|
||||
|
||||
//DEFINE_LOG_CATEGORY(LogViveCustomHandGesture);
|
||||
|
||||
|
||||
FCustomHandGestureNative UViveCustomGestureComponent::OnCustomHandGestureNative_Right;
|
||||
FCustomHandGestureNative UViveCustomGestureComponent::OnCustomHandGestureNative_Left;
|
||||
|
||||
FDualGestureNative UViveCustomGestureComponent::OnCustomHandGestureNative_Dual;
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UViveCustomGestureComponent::UViveCustomGestureComponent()
|
||||
{
|
||||
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
|
||||
// off to improve performance if you don't need them.
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
|
||||
// Called when the game starts
|
||||
void UViveCustomGestureComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
UViveCustomGestureComponent::OnCustomHandGestureNative_Right.AddDynamic(this, &UViveCustomGestureComponent::OnCustomHandGestureHandling_Right);
|
||||
UViveCustomGestureComponent::OnCustomHandGestureNative_Left.AddDynamic(this, &UViveCustomGestureComponent::OnCustomHandGestureHandling_Left);
|
||||
|
||||
UViveCustomGestureComponent::OnCustomHandGestureNative_Dual.AddDynamic(this, &UViveCustomGestureComponent::OnDualGestureHandling);
|
||||
}
|
||||
|
||||
void UViveCustomGestureComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void UViveCustomGestureComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
}
|
||||
|
||||
#pragma region
|
||||
void UViveCustomGestureComponent::OnCustomHandGestureHandling_Right(FString gesture)
|
||||
{
|
||||
UE_LOG(LogViveCustomHandGesture, Log, TEXT("OnCustomHandGestureHandling_Right() gesture: %s"), *gesture);
|
||||
OnCustomHandGestureChanged_Right.Broadcast(gesture);
|
||||
}
|
||||
|
||||
void UViveCustomGestureComponent::OnCustomHandGestureHandling_Left(FString gesture)
|
||||
{
|
||||
UE_LOG(LogViveCustomHandGesture, Log, TEXT("OnCustomHandGestureHandling_Left() gesture: %s"), *gesture);
|
||||
OnCustomHandGestureChanged_Left.Broadcast(gesture);
|
||||
}
|
||||
|
||||
void UViveCustomGestureComponent::OnDualGestureHandling(FString gesture)
|
||||
{
|
||||
UE_LOG(LogViveCustomHandGesture, Log, TEXT("OnDualGestureHandling() gesture: %s"), *gesture);
|
||||
OnCustomHandGestureChange_DualHand.Broadcast(gesture);
|
||||
}
|
||||
#pragma endregion Custom Gesture
|
||||
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveCustomHandGestureModule.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
//DEFINE_LOG_CATEGORY(LogViveCustomHandGesture);
|
||||
IMPLEMENT_MODULE(FViveCustomHandGestureModule, ViveCustomHandGesture)
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleInterface.h"
|
||||
|
||||
//DECLARE_LOG_CATEGORY_EXTERN(LogViveCustomHandGesture, Log, All);
|
||||
|
||||
class FViveCustomHandGestureModule : public IModuleInterface
|
||||
{
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Math/Vector.h"
|
||||
#include "ViveCustomHandGestureComponent.h"
|
||||
#include "ViveCustomHandGestureUtils.h"
|
||||
#include "ViveCustomHandGesture.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogViveCustomHandGesture, Log, All);
|
||||
|
||||
using namespace wvr::hand;
|
||||
|
||||
struct EulerAngles {
|
||||
float roll, pitch, yaw;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class VIVECUSTOMHANDGESTURE_API AViveCustomHandGesture : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this actor's properties
|
||||
AViveCustomHandGesture();
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
public:
|
||||
const FString kUnknownGesture = TEXT("Unknown");
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
TArray< FSingleHandGesture > LeftGestures;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
TArray< FSingleHandGesture > RightGestures;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
TArray< FDualHandGesture > DualHandGestures;
|
||||
|
||||
private:
|
||||
bool validPoseLeft = false, validPoseRight = false;
|
||||
TArray< FVector > s_JointPositionsLeft, s_JointPositionsRight;
|
||||
TArray< FQuat > s_JointRotationsLeft, s_JointRotationsRight;
|
||||
|
||||
EWaveThumbState m_ThumbStateLeft = EWaveThumbState::None, m_ThumbStateRight = EWaveThumbState::None;
|
||||
EWaveFingerState m_IndexStateLeft = EWaveFingerState::None, m_IndexStateRight = EWaveFingerState::None;
|
||||
EWaveFingerState m_MiddleStateLeft = EWaveFingerState::None, m_MiddleStateRight = EWaveFingerState::None;
|
||||
EWaveFingerState m_RingStateLeft = EWaveFingerState::None, m_RingStateRight = EWaveFingerState::None;
|
||||
EWaveFingerState m_PinkyStateLeft = EWaveFingerState::None, m_PinkyStateRight = EWaveFingerState::None;
|
||||
|
||||
void UpdateFingerState();
|
||||
EWaveThumbState GetThumbState(FVector root, FVector node1, FVector top, bool isLeft);
|
||||
EWaveFingerState GetFingerState(FVector root, FVector node1, FVector top, bool isLeft);
|
||||
bool MatchThumbState(FThumbState state);
|
||||
bool MatchRotationSingle(FRotator rotateCondition, bool isLeft);
|
||||
|
||||
bool MatchDistanceSingle(EWaveHandJoint node1, EWaveHandJoint node2, EWaveJointDistance distance, bool isLeft);
|
||||
bool MatchDistanceDual(EWaveHandJoint leftNode, EWaveHandJoint rightNode, EWaveJointDistance distance);
|
||||
|
||||
FString m_LeftHandGesture = TEXT("Default"), m_RightHandGesture = TEXT("Default");
|
||||
bool MatchGestureSingle(FSingleHandSetting gesture, bool isLeft);
|
||||
FString m_DualHandGesture = TEXT("Default");
|
||||
bool MatchGestureDual(FDualHandSetting gesture);
|
||||
EulerAngles ToEulerAnglesDegree(FQuat q);
|
||||
};
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "ViveCustomHandGestureUtils.h"
|
||||
#include "ViveCustomHandGestureComponent.generated.h"
|
||||
|
||||
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCustomHandGestureNative, FString, gesture);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCustomHandGestureBp, FString, gesture);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDualGestureNative, FString, gesture);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDualGestureBp, FString, gesture);
|
||||
|
||||
|
||||
UCLASS( ClassGroup=(Vive), meta=(BlueprintSpawnableComponent) )
|
||||
class VIVECUSTOMHANDGESTURE_API UViveCustomGestureComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this component's properties
|
||||
UViveCustomGestureComponent();
|
||||
|
||||
protected:
|
||||
// Called when the game starts
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
|
||||
public:
|
||||
#pragma region
|
||||
/// Single Hand
|
||||
static FCustomHandGestureNative OnCustomHandGestureNative_Left;
|
||||
static FCustomHandGestureNative OnCustomHandGestureNative_Right;
|
||||
|
||||
UFUNCTION()
|
||||
void OnCustomHandGestureHandling_Left(FString gesture);
|
||||
UFUNCTION()
|
||||
void OnCustomHandGestureHandling_Right(FString gesture);
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Vive|CustomHandGesture")
|
||||
FCustomHandGestureBp OnCustomHandGestureChanged_Left;
|
||||
UPROPERTY(BlueprintAssignable, Category = "Vive|CustomHandGesture")
|
||||
FCustomHandGestureBp OnCustomHandGestureChanged_Right;
|
||||
|
||||
/// Dual Hand
|
||||
static FDualGestureNative OnCustomHandGestureNative_Dual;
|
||||
|
||||
UFUNCTION()
|
||||
void OnDualGestureHandling(FString gesture);
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Vive|CustomHandGesture")
|
||||
FDualGestureBp OnCustomHandGestureChange_DualHand;
|
||||
#pragma endregion Custom Gesture
|
||||
|
||||
};
|
||||
@@ -1,254 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ViveCustomHandGestureUtils.generated.h"
|
||||
|
||||
UENUM(BlueprintType, Category = "Vive|CustomHandGesture")
|
||||
enum class EWaveGestureType : uint8
|
||||
{
|
||||
Invalid = 0,//WVR_HandGestureType::WVR_HandGestureType_Invalid, /**< The gesture is invalid. */
|
||||
Unknown = 1,//WVR_HandGestureType::WVR_HandGestureType_Unknown, /**< Unknown gesture type. */
|
||||
Fist = 2,//WVR_HandGestureType::WVR_HandGestureType_Fist, /**< Represent fist gesture. */
|
||||
Five = 3,//WVR_HandGestureType::WVR_HandGestureType_Five, /**< Represent five gesture. */
|
||||
OK = 4,//WVR_HandGestureType::WVR_HandGestureType_OK, /**< Represent ok gesture. */
|
||||
Like = 5,//WVR_HandGestureType::WVR_HandGestureType_ThumbUp, /**< Represent thumb up gesture. */
|
||||
Point = 6,//WVR_HandGestureType::WVR_HandGestureType_IndexUp, /**< Represent index up gesture. */
|
||||
Palm_Pinch = 7,//WVR_HandGestureType::WVR_HandGestureType_Palm_Pinch,
|
||||
Yeah = 8,//WVR_HandGestureType::WVR_HandGestureType_Yeah /**< Represent yeah gesture. */
|
||||
};
|
||||
|
||||
static const unsigned int EWaveHandJointCount = 26;
|
||||
UENUM(BlueprintType, Category = "Vive|CustomHandGesture")
|
||||
enum class EWaveHandJoint : uint8
|
||||
{
|
||||
Palm = 0,//WVR_HandJoint::WVR_HandJoint_Palm,
|
||||
Wrist = 1,//WVR_HandJoint::WVR_HandJoint_Wrist,
|
||||
Thumb_Joint0 = 2,//WVR_HandJoint::WVR_HandJoint_Thumb_Joint0,
|
||||
Thumb_Joint1 = 3,//WVR_HandJoint::WVR_HandJoint_Thumb_Joint1,
|
||||
Thumb_Joint2 = 4,//WVR_HandJoint::WVR_HandJoint_Thumb_Joint2, // 5
|
||||
Thumb_Tip = 5,//WVR_HandJoint::WVR_HandJoint_Thumb_Tip,
|
||||
Index_Joint0 = 6,//WVR_HandJoint::WVR_HandJoint_Index_Joint0,
|
||||
Index_Joint1 = 7,//WVR_HandJoint::WVR_HandJoint_Index_Joint1,
|
||||
Index_Joint2 = 8,//WVR_HandJoint::WVR_HandJoint_Index_Joint2,
|
||||
Index_Joint3 = 9,//WVR_HandJoint::WVR_HandJoint_Index_Joint3, // 10
|
||||
Index_Tip = 10,//WVR_HandJoint::WVR_HandJoint_Index_Tip,
|
||||
Middle_Joint0 = 11,//WVR_HandJoint::WVR_HandJoint_Middle_Joint0,
|
||||
Middle_Joint1 = 12,//WVR_HandJoint::WVR_HandJoint_Middle_Joint1,
|
||||
Middle_Joint2 = 13,//WVR_HandJoint::WVR_HandJoint_Middle_Joint2,
|
||||
Middle_Joint3 = 14,//WVR_HandJoint::WVR_HandJoint_Middle_Joint3, // 15
|
||||
Middle_Tip = 15,//WVR_HandJoint::WVR_HandJoint_Middle_Tip,
|
||||
Ring_Joint0 = 16,//WVR_HandJoint::WVR_HandJoint_Ring_Joint0,
|
||||
Ring_Joint1 = 17,//WVR_HandJoint::WVR_HandJoint_Ring_Joint1,
|
||||
Ring_Joint2 = 18,//WVR_HandJoint::WVR_HandJoint_Ring_Joint2,
|
||||
Ring_Joint3 = 19,//WVR_HandJoint::WVR_HandJoint_Ring_Joint3, // 20
|
||||
Ring_Tip = 20,//WVR_HandJoint::WVR_HandJoint_Ring_Tip,
|
||||
Pinky_Joint0 = 21,//WVR_HandJoint::WVR_HandJoint_Pinky_Joint0,
|
||||
Pinky_Joint1 = 22,//WVR_HandJoint::WVR_HandJoint_Pinky_Joint1,
|
||||
Pinky_Joint2 = 23,//WVR_HandJoint::WVR_HandJoint_Pinky_Joint2,
|
||||
Pinky_Joint3 = 24,//WVR_HandJoint::WVR_HandJoint_Pinky_Joint3, // 25
|
||||
Pinky_Tip = 25,//WVR_HandJoint::WVR_HandJoint_Pinky_Tip,
|
||||
};
|
||||
|
||||
|
||||
UENUM(BlueprintType, Category = "Vive|CustomHandGesture")
|
||||
enum class EWaveThumbState : uint8
|
||||
{
|
||||
None = 0,
|
||||
Close = 1,
|
||||
Open = 2,
|
||||
};
|
||||
|
||||
UENUM(BlueprintType, Category = "Vive|CustomHandGesture")
|
||||
enum class EWaveFingerState : uint8
|
||||
{
|
||||
None = 0,
|
||||
Close = 1,
|
||||
Relax = 2,
|
||||
Open = 4,
|
||||
};
|
||||
|
||||
UENUM(BlueprintType, Category = "Vive|CustomHandGesture")
|
||||
enum class EWaveJointDistance : uint8
|
||||
{
|
||||
Near = 0,
|
||||
Far,
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FThumbState
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
bool Close = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
bool Open = false;
|
||||
|
||||
public:
|
||||
uint64_t State()
|
||||
{
|
||||
return (Close ? 1 << (uint8)EWaveThumbState::Close : 0)
|
||||
| (Open ? 1 << (uint8)EWaveThumbState::Open : 0);
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FFingerState
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
bool Close = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
bool Relax = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
bool Open = false;
|
||||
|
||||
public:
|
||||
uint64_t State()
|
||||
{
|
||||
return (Close ? 1 << (uint8)EWaveFingerState::Close : 0)
|
||||
| (Relax ? 1 << (uint8)EWaveFingerState::Relax : 0)
|
||||
| (Open ? 1 << (uint8)EWaveFingerState::Open : 0);
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSingleHandNodeDistance
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
EWaveHandJoint Node1 = EWaveHandJoint::Palm;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ViveR|CustomHandGesture")
|
||||
EWaveHandJoint Node2 = EWaveHandJoint::Palm;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
EWaveJointDistance Distance = EWaveJointDistance::Near;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSingleHandSetting
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FThumbState Thumb;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FFingerState Index;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FFingerState Middle;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FFingerState Ring;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FFingerState Pinky;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
TArray< FSingleHandNodeDistance > SingleHandNodeDistances;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FRotator RotationCondition = FRotator::ZeroRotator;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSingleHandGesture
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FString Name = TEXT("Default");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FSingleHandSetting Setting;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FDualHandNodeDistance
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
EWaveHandJoint LeftNode = EWaveHandJoint::Palm;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
EWaveHandJoint RightNode = EWaveHandJoint::Palm;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
EWaveJointDistance Distance = EWaveJointDistance::Near;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FDualHandSetting
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FSingleHandSetting LeftHand;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FSingleHandSetting RightHand;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
TArray< FDualHandNodeDistance > DualHandNodeDistances;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FDualHandGesture
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FString Name = TEXT("Default");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vive|CustomHandGesture")
|
||||
FDualHandSetting Setting;
|
||||
};
|
||||
|
||||
namespace wvr {
|
||||
namespace hand {
|
||||
class WaveHandHelper {
|
||||
public:
|
||||
static float VectorAngle(FVector a, FVector b)
|
||||
{
|
||||
float angleCosine = FVector::DotProduct(a, b) / (a.Size() * b.Size());
|
||||
float angleRadians = FMath::Acos(angleCosine);
|
||||
return FMath::RadiansToDegrees(angleRadians);
|
||||
}
|
||||
static EWaveThumbState GetThumbState(FVector root, FVector node1, FVector top)
|
||||
{
|
||||
FVector a = node1 - root, b = top - node1;
|
||||
float angle = VectorAngle(a, b);
|
||||
|
||||
return angle < 15 ? EWaveThumbState::Open : EWaveThumbState::Close;
|
||||
}
|
||||
static EWaveFingerState GetFingerState(FVector root, FVector node1, FVector top)
|
||||
{
|
||||
FVector a = node1 - root, b = top - node1;
|
||||
float angle = VectorAngle(a, b);
|
||||
|
||||
if (angle < 25) return EWaveFingerState::Open;
|
||||
if (angle > 75) return EWaveFingerState::Close;
|
||||
|
||||
return EWaveFingerState::Relax;
|
||||
}
|
||||
static bool MatchThumbState(FThumbState state, EWaveThumbState thumb)
|
||||
{
|
||||
uint32_t value = 1 << (uint8)thumb;
|
||||
if ((state.State() & value) != 0) { return true; }
|
||||
return false;
|
||||
}
|
||||
static bool MatchFingerState(FFingerState state, EWaveFingerState finger)
|
||||
{
|
||||
uint32_t value = 1 << (uint8)finger;
|
||||
if ((state.State() & value) != 0) { return true; }
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
namespace UnrealBuildTool.Rules
|
||||
{
|
||||
public class ViveCustomHandGesture : ModuleRules
|
||||
{
|
||||
public ViveCustomHandGesture(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Core",
|
||||
"HeadMountedDisplay"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,496 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveOpenXRAnchorAsyncAction.h"
|
||||
#include "HAL/FileManager.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "ARBlueprintLibrary.h"
|
||||
#include "ViveOpenXRAnchorModule.h"
|
||||
#include "ViveOpenXRAnchorFunctionLibrary.h"
|
||||
#include "ViveOpenXRAnchorPersistence.h"
|
||||
#include "Kismet/BlueprintPathsLibrary.h"
|
||||
|
||||
/*
|
||||
UViveOpenXRAnchorExportAsyncAction* UViveOpenXRAnchorExportAsyncAction::ExportPersistedARPin(UObject* WorldContextObject, UARPin* AR_Pin, const FString folderName)
|
||||
{
|
||||
UViveOpenXRAnchorExportAsyncAction* BlueprintNode = NewObject<UViveOpenXRAnchorExportAsyncAction>(WorldContextObject);
|
||||
BlueprintNode->WorldContextObject = WorldContextObject;
|
||||
BlueprintNode->mARPin = AR_Pin;
|
||||
BlueprintNode->mFolder = folderName;
|
||||
// Register with the game instance to avoid being garbage collected
|
||||
BlueprintNode->RegisterWithGameInstance(WorldContextObject);
|
||||
return BlueprintNode;
|
||||
}
|
||||
|
||||
void UViveOpenXRAnchorExportAsyncAction::Activate()
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("ExportPersistedARPin start"));
|
||||
if (mFileName == "") mFileName = mARPin->GetDebugName().ToString();
|
||||
|
||||
// check ar pin saved(persisted) or not
|
||||
// if not, persisted first. Call UARBlueprintLibrary::SaveARPinToLocalStore(FName InSaveName, UARPin* InPin)
|
||||
UARBlueprintLibrary::SaveARPinToLocalStore(mARPin->GetDebugName(), mARPin);
|
||||
|
||||
// export persisted anchor data
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAsyncAction] Failed to get FViveOpenXRAnchor instance."));
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
XrFutureEXT future = XR_NULL_HANDLE;
|
||||
XrResult result = mod->AcquirePersistedAnchorCollectionAsync(&future);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAsyncAction] Failed to acquire persisted anchor collection async: ret=%d"), result);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
UPersistedAnchorCollection* PACollection = UPersistedAnchorCollection::StartAcquirePACTask(future, GetTransientPackage());
|
||||
if (PACollection == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAsyncAction] Failed to StartAcquirePACTask."), result);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
PACollection->WaitFuture();
|
||||
|
||||
//task = PACollection->ExportPersistedAnchor(mFileName);
|
||||
|
||||
if (task == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAsyncAction] Failed to ExportPersistedAnchor."), result);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
task->WaitTask();
|
||||
|
||||
// Save to external storage
|
||||
TArray<uint8> exported_data;
|
||||
bool isSuccess;
|
||||
XrResult xrResult;
|
||||
task->GetResult(exported_data, isSuccess, (int&)xrResult);
|
||||
if (!isSuccess)
|
||||
{
|
||||
task->MarkAsGarbage();
|
||||
|
||||
// ToDo print failure XrResult;
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("ExportPersistedARPin XrResult failed "));
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
FString anchorName = task->GetPersistedAnchorName();
|
||||
FString anchorFileName = UBlueprintPathsLibrary::MakeValidFileName(anchorName, "_");
|
||||
anchorFileName = UBlueprintPathsLibrary::SetExtension(anchorFileName, "pa");
|
||||
FString dir = UViveOpenXRAnchorFunctionLibrary::GetExternalStorageDir();
|
||||
FString saved_filepath = dir + mFolder + "/" + anchorFileName;
|
||||
|
||||
task->MarkAsGarbage();
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("ExportPersistedARPin end"));
|
||||
|
||||
if (FFileHelper::SaveArrayToFile(exported_data, *saved_filepath))
|
||||
{
|
||||
// call complete when done
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("ExportPersistedARPin success"));
|
||||
Complete.Broadcast(saved_filepath);
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("ExportPersistedARPin failed"));
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
UViveOpenXRAnchorExportAllAsyncAction* UViveOpenXRAnchorExportAllAsyncAction::ExportAllPersistedARPins(UObject* WorldContextObject, const FString folderName)
|
||||
{
|
||||
UViveOpenXRAnchorExportAllAsyncAction* BlueprintNode = NewObject<UViveOpenXRAnchorExportAllAsyncAction>(WorldContextObject);
|
||||
BlueprintNode->WorldContextObject = WorldContextObject;
|
||||
BlueprintNode->mFolder = folderName;
|
||||
|
||||
// Register with the game instance to avoid being garbage collected
|
||||
BlueprintNode->RegisterWithGameInstance(WorldContextObject);
|
||||
return BlueprintNode;
|
||||
}
|
||||
|
||||
void UViveOpenXRAnchorExportAllAsyncAction::Activate()
|
||||
{
|
||||
tasks.Empty();
|
||||
|
||||
FString dir = UViveOpenXRAnchorFunctionLibrary::GetExternalStorageDir();
|
||||
FString saved_folder = dir + mFolder;
|
||||
|
||||
// Ensure the folder path is correctly formatted with a trailing slash
|
||||
FString FixedFolderPath = saved_folder;
|
||||
if (!FixedFolderPath.EndsWith(TEXT("/")))
|
||||
{
|
||||
FixedFolderPath += TEXT("/");
|
||||
}
|
||||
|
||||
// Check if the directory exists
|
||||
if (!IFileManager::Get().DirectoryExists(*FixedFolderPath))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Directory does not exist: %s"), *FixedFolderPath);
|
||||
|
||||
// Attempt to create the directory
|
||||
bool bDirectoryCreated = IFileManager::Get().MakeDirectory(*FixedFolderPath, true);
|
||||
|
||||
if (bDirectoryCreated)
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Directory created successfully: %s"), *FixedFolderPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Failed to create directory: %s"), *FixedFolderPath);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Directory already exists: %s"), *FixedFolderPath);
|
||||
}
|
||||
|
||||
if (saved_folder.IsEmpty())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Folder path is empty."));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Failed Export PersistedARPins because directory is not empty."));
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
|
||||
// Find all files in the specified folder
|
||||
/*
|
||||
TArray<FString> FoundFiles;
|
||||
IFileManager& FileManager = IFileManager::Get();
|
||||
FileManager.FindFiles(FoundFiles, *FixedFolderPath, TEXT("*")); // Use "*" to find all files
|
||||
UE_LOG(LogTemp, Log, TEXT("Find all files under: %s"), *FixedFolderPath);
|
||||
UE_LOG(LogTemp, Log, TEXT("FoundFiles: %d"), FoundFiles.Num());
|
||||
// Delete each file found in the folder
|
||||
for (const FString& FileName : FoundFiles)
|
||||
{
|
||||
FString FullFilePath = FixedFolderPath + FileName;
|
||||
bool bDeleted = FileManager.Delete(*FullFilePath);
|
||||
|
||||
if (bDeleted)
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("Deleted file: %s"), *FullFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Failed to delete file: %s"), *FullFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("Successfully deleted all files in folder: %s"), *FixedFolderPath);
|
||||
*/
|
||||
}
|
||||
|
||||
XrResult result;
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
|
||||
if (!mod)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Failed to get FViveOpenXRAnchor instance."));
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
XrFutureEXT future = XR_NULL_HANDLE;
|
||||
result = mod->AcquirePersistedAnchorCollectionAsync(&future);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Failed to acquire persisted anchor collection async: ret = %d"), result);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
UPersistedAnchorCollection* PACollection = UPersistedAnchorCollection::StartAcquirePACTask(future, GetTransientPackage());
|
||||
if (PACollection == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Failed to StartAcquirePACTask."), result);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
auto pins = UARBlueprintLibrary::GetAllPins();
|
||||
UE_LOG(LogTemp, Log, TEXT("pins: %d"), pins.Num());
|
||||
|
||||
for (auto pin : pins)
|
||||
{
|
||||
|
||||
FString paName = pin->GetDebugName().ToString();
|
||||
if (!paName.EndsWith(TEXT("_PA")))
|
||||
{
|
||||
paName += TEXT("_PA");
|
||||
}
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Export AR Pin Debug Name: [%s], Persisted Anchor Name: [%s]"), *pin->GetDebugName().ToString(), *paName);
|
||||
|
||||
// export persisted anchor data
|
||||
UExportTask* task = PACollection->ExportPersistedAnchor(paName, FixedFolderPath);
|
||||
if (task)
|
||||
{
|
||||
tasks.Add(task);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Fail to export AR Pin: %s"), *pin->GetDebugName().ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// wait for all task
|
||||
for (auto task : tasks)
|
||||
{
|
||||
task->WaitTask();
|
||||
}
|
||||
|
||||
for (auto task : tasks)
|
||||
{
|
||||
// Todo check success or failure
|
||||
TArray<uint8> exported_data;
|
||||
bool isSuccess;
|
||||
XrResult xrResult;
|
||||
task->GetResult(exported_data, isSuccess, (int&)xrResult);
|
||||
if (!isSuccess)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Fail to export AR Pin: %s"), *task->GetPersistedAnchorName());
|
||||
}
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Exported AR Pin: %s"), *task->GetPersistedAnchorName());
|
||||
task->MarkAsGarbage();
|
||||
}
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("[UViveOpenXRAnchorExportAllAsyncAction] Exported all AR Pins to folder: %s"), *FixedFolderPath);
|
||||
Complete.Broadcast(FixedFolderPath);
|
||||
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
/*
|
||||
UViveOpenXRAnchorImportAsyncAction* UViveOpenXRAnchorImportAsyncAction::ImportPersistedARPinFromFile(UObject* WorldContextObject, const FString filePath)
|
||||
{
|
||||
UViveOpenXRAnchorImportAsyncAction* BlueprintNode = NewObject<UViveOpenXRAnchorImportAsyncAction>(WorldContextObject);
|
||||
BlueprintNode->WorldContextObject = WorldContextObject;
|
||||
BlueprintNode->mFilepath = filePath;
|
||||
// Register with the game instance to avoid being garbage collected
|
||||
BlueprintNode->RegisterWithGameInstance(WorldContextObject);
|
||||
return BlueprintNode;
|
||||
}
|
||||
|
||||
void UViveOpenXRAnchorImportAsyncAction::Activate()
|
||||
{
|
||||
if (mFilepath == "")
|
||||
{
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
return;
|
||||
}
|
||||
|
||||
// load file
|
||||
TArray<uint8> data;
|
||||
if (!FFileHelper::LoadFileToArray(data, *mFilepath))
|
||||
{
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
return;
|
||||
}
|
||||
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorImportAsyncAction] Failed to get FViveOpenXRAnchor instance."));
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
XrFutureEXT future = XR_NULL_HANDLE;
|
||||
XrResult result = mod->AcquirePersistedAnchorCollectionAsync(&future);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorImportAsyncAction] Failed to acquire persisted anchor collection async: ret=%d"), result);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
UPersistedAnchorCollection* PACollection = UPersistedAnchorCollection::StartAcquirePACTask(future, GetTransientPackage());
|
||||
if (PACollection == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorImportAsyncAction] Failed to StartAcquirePACTask."), result);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
// import persisted anchor data
|
||||
anchorName = UBlueprintPathsLibrary::GetBaseFilename(mFilepath);
|
||||
// task = PACollection->ImportPersistedAnchor(anchorName, data);
|
||||
|
||||
if (task)
|
||||
{
|
||||
task->WaitTask();
|
||||
|
||||
bool isSuccess;
|
||||
XrResult xrResult;
|
||||
task->GetResult(isSuccess, (int&)xrResult);
|
||||
|
||||
|
||||
if (!isSuccess)
|
||||
{
|
||||
task->MarkAsGarbage();
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
return;
|
||||
}
|
||||
|
||||
// Create anchor from loaded persisted anchor
|
||||
|
||||
// call complete when done
|
||||
// Complete.Broadcast(ARPin);
|
||||
// SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
UViveOpenXRAnchorImportAllAsyncAction* UViveOpenXRAnchorImportAllAsyncAction::ImportAllPersistedARPinsFromFolder(UObject* WorldContextObject, const FString folderPath)
|
||||
{
|
||||
UViveOpenXRAnchorImportAllAsyncAction* BlueprintNode = NewObject<UViveOpenXRAnchorImportAllAsyncAction>(WorldContextObject);
|
||||
BlueprintNode->WorldContextObject = WorldContextObject;
|
||||
BlueprintNode->mFolderpath = folderPath;
|
||||
// Register with the game instance to avoid being garbage collected
|
||||
BlueprintNode->RegisterWithGameInstance(WorldContextObject);
|
||||
return BlueprintNode;
|
||||
}
|
||||
|
||||
void UViveOpenXRAnchorImportAllAsyncAction::Activate()
|
||||
{
|
||||
// Ensure the folder path is correctly formatted with a trailing slash
|
||||
if (!mFolderpath.EndsWith(TEXT("/")))
|
||||
{
|
||||
mFolderpath += TEXT("/");
|
||||
}
|
||||
|
||||
// If path is not full path, combine with external storage path.
|
||||
FString saved_folder = mFolderpath;
|
||||
if (!IFileManager::Get().DirectoryExists(*mFolderpath))
|
||||
{
|
||||
FString externalDir = UViveOpenXRAnchorFunctionLibrary::GetExternalStorageDir();
|
||||
saved_folder = externalDir + mFolderpath;
|
||||
}
|
||||
|
||||
FString FixedFolderPath = saved_folder;
|
||||
|
||||
// Check if the directory exists
|
||||
if (!IFileManager::Get().DirectoryExists(*FixedFolderPath))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Directory does not exist: %s"), *FixedFolderPath);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
// Check if folder path is empty
|
||||
if (saved_folder.IsEmpty())
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Folder path is empty."));
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
// Find all files in the folder
|
||||
TArray<FString> FoundFiles;
|
||||
IFileManager& FileManager = IFileManager::Get();
|
||||
FileManager.FindFiles(FoundFiles, *FixedFolderPath, TEXT("*.bin")); // Use "*" to find all files
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Import Found: %d"), FoundFiles.Num());
|
||||
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Failed to get FViveOpenXRAnchor instance."));
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
XrFutureEXT future = XR_NULL_HANDLE;
|
||||
XrResult result = mod->AcquirePersistedAnchorCollectionAsync(&future);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Failed to acquire persisted anchor collection async: ret=%d"), result);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
UPersistedAnchorCollection* PACollection = UPersistedAnchorCollection::StartAcquirePACTask(future, GetTransientPackage());
|
||||
if (PACollection == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Failed to StartAcquirePACTask."), result);
|
||||
Failure.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
TArray<FString> ExistAnchorNames = TArray<FString>();
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Check anchor names"));
|
||||
PACollection->EnumeratePersistedAnchorNames(ExistAnchorNames);
|
||||
for (FString str : ExistAnchorNames)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Current exists persisted anchor name: %s"), *str);
|
||||
}
|
||||
|
||||
TArray<FString> filenames;
|
||||
FString ExtName = TEXT("*.bin");
|
||||
UViveOpenXRAnchorFunctionLibrary::GetFilesInFolder(FixedFolderPath, ExtName, filenames);
|
||||
UE_LOG(LogTemp, Log, TEXT("filenames: %d"), filenames.Num());
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Import all AR Pins from folder: %s"), *FixedFolderPath);
|
||||
|
||||
for (FString filename : filenames)
|
||||
{
|
||||
FString CheckImportAnchorName = filename;
|
||||
CheckImportAnchorName = CheckImportAnchorName.Replace(TEXT(".bin"), TEXT("")); // Removes ".bin"
|
||||
UE_LOG(LogTemp, Log, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] CheckImportAnchorName: %s"), *CheckImportAnchorName);
|
||||
|
||||
if (!ExistAnchorNames.Contains(CheckImportAnchorName))
|
||||
{
|
||||
TArray<uint8> data;
|
||||
FString filePath = FixedFolderPath + filename;
|
||||
if (FFileHelper::LoadFileToArray(data, *filePath))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Import AR Pin from file: %s"), *filePath);
|
||||
auto task = PACollection->ImportPersistedAnchor(filename, data, filePath);
|
||||
if (task)
|
||||
{
|
||||
tasks.Add(task);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Fail to import AR Pin from file: %s"), *filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] ImportAnchor: %s already exists in collection"), *CheckImportAnchorName);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto task : tasks)
|
||||
{
|
||||
task->WaitTask();
|
||||
}
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("[UViveOpenXRAnchorImportAllAsyncAction] Load all AR Pins from local store."));
|
||||
auto ARPinsMap = UARBlueprintLibrary::LoadARPinsFromLocalStore();
|
||||
ARPinsMap.GenerateValueArray(pins);
|
||||
Complete.Broadcast(pins);
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
// ViveOpenXRAnchorFunctionLibrary.cpp
|
||||
// Copyright (c) 2024 HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveOpenXRAnchorAsyncTask.h"
|
||||
#include "ViveOpenXRAnchorModule.h"
|
||||
#include "ViveOpenXRAnchorFunctionLibrary.h"
|
||||
#include "ViveOpenXRAnchorPersistence.h"
|
||||
#include "HAL/PlatformFileManager.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
|
||||
UExportTask::UExportTask()
|
||||
: collectionObj(nullptr), result((int)XR_ERROR_VALIDATION_FAILURE)
|
||||
{
|
||||
}
|
||||
|
||||
UExportTask* UExportTask::StartExportTask(UPersistedAnchorCollection* collection, const FString& anchorName, UObject* outer, const FString& folderName)
|
||||
{
|
||||
if (!outer) outer = GetTransientPackage();
|
||||
UExportTask* task = NewObject<UExportTask>(outer);
|
||||
task->collectionObj = collection;
|
||||
task->persistedAnchorName = anchorName;
|
||||
task->folderPath = folderName;
|
||||
AddTaskToQueue(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
void UExportTask::ExecuteTask()
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("ExportPersistedAnchor %s"), *persistedAnchorName);
|
||||
XrResult ret = XR_ERROR_VALIDATION_FAILURE;
|
||||
uint32_t count = 0;
|
||||
do {
|
||||
if (collectionObj == nullptr) break;
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) break;
|
||||
auto collection = (XrPersistedAnchorCollectionHTC)collectionObj->GetCollection();
|
||||
if (collection == XR_NULL_HANDLE) break;
|
||||
|
||||
auto xrName = FViveOpenXRAnchor::FromFString(persistedAnchorName);
|
||||
//UE_LOG(ViveOXRAnchor, Log, TEXT("ExportPersistedAnchor1 %s +++"), *persistedAnchorName);
|
||||
ret = mod->ExportPersistedAnchor(collection, &xrName, 0, &count, nullptr);
|
||||
if (XR_FAILED(ret) || count == 0) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("ExportPersistedAnchor1 %s failed"), *persistedAnchorName);
|
||||
break;
|
||||
}
|
||||
|
||||
data.SetNum(count);
|
||||
ret = mod->ExportPersistedAnchor(collection, &xrName, count, &count, (char*)data.GetData());
|
||||
} while (false);
|
||||
|
||||
// Not to expose openxr result type to function library header.
|
||||
if (XR_FAILED(ret) || count == 0) {
|
||||
data.SetNum(0);
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to export persisted anchor: %s ret=%d, count=%d"), *persistedAnchorName, ret, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create file name
|
||||
FString fileName = persistedAnchorName + TEXT(".bin");
|
||||
|
||||
// Construct the full file path
|
||||
FString filePath = folderPath + fileName;
|
||||
|
||||
// Save the data to the file
|
||||
if (FFileHelper::SaveArrayToFile(data, *filePath))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("File saved successfully at: %s"), *filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to save file at: %s"), *filePath);
|
||||
}
|
||||
}
|
||||
|
||||
// Check weak again because the task take too long to process.
|
||||
result = (int32)ret;
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Export result: %s ret=%d, count=%d"), *persistedAnchorName, ret, count);
|
||||
}
|
||||
|
||||
void UExportTask::GetResult(TArray<uint8>& dataOut, bool& isSuccess, int& xrResult) const
|
||||
{
|
||||
dataOut = data;
|
||||
isSuccess = result == XR_SUCCESS;
|
||||
xrResult = result;
|
||||
}
|
||||
|
||||
FString UExportTask::GetPersistedAnchorName() const
|
||||
{
|
||||
return persistedAnchorName;
|
||||
}
|
||||
|
||||
UImportTask::UImportTask()
|
||||
: collectionObj(nullptr), result((int)XR_ERROR_VALIDATION_FAILURE)
|
||||
{
|
||||
}
|
||||
|
||||
UImportTask* UImportTask::StartImportTask(UPersistedAnchorCollection* collection, const TArray<uint8>& data, UObject* outer, const FString& filePath)
|
||||
{
|
||||
if (!outer) outer = GetTransientPackage();
|
||||
UImportTask* task = NewObject<UImportTask>(outer);
|
||||
task->collectionObj = collection;
|
||||
task->data = data;
|
||||
task->filePath = filePath;
|
||||
AddTaskToQueue(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
void UImportTask::SetTaskName(const FString& name)
|
||||
{
|
||||
UImportTask::taskName = name;
|
||||
}
|
||||
|
||||
FString UImportTask::GetTaskName() const
|
||||
{
|
||||
return taskName;
|
||||
}
|
||||
|
||||
void UImportTask::ExecuteTask()
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("ImportPersistedAnchor: taskName=%s"), *taskName);
|
||||
|
||||
// Check if the file exists
|
||||
if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*filePath))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Warning, TEXT("File does not exist: %s"), *filePath);
|
||||
}
|
||||
|
||||
XrResult ret = XR_ERROR_VALIDATION_FAILURE;
|
||||
do {
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) break;
|
||||
if (collectionObj == nullptr) break;
|
||||
auto collection = (XrPersistedAnchorCollectionHTC)collectionObj->GetCollection();
|
||||
if (collection == XR_NULL_HANDLE) break;
|
||||
|
||||
ret = mod->ImportPersistedAnchor(collection, data.Num(), (char*)data.GetData());
|
||||
} while (false);
|
||||
|
||||
if (XR_FAILED(ret)) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to import persisted anchor: taskName=%s ret=%d"), *taskName, ret);
|
||||
}
|
||||
|
||||
result = (int)ret;
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Import result: taskName=%s ret=%d"), *taskName, ret);
|
||||
}
|
||||
|
||||
void UImportTask::GetResult(bool& isSuccess, int& xrResult) const
|
||||
{
|
||||
isSuccess = result == XR_SUCCESS;
|
||||
xrResult = result;
|
||||
}
|
||||
|
||||
UPersistTask::UPersistTask() : anchor(0), result((int)XR_ERROR_VALIDATION_FAILURE)
|
||||
{
|
||||
}
|
||||
|
||||
UPersistTask* UPersistTask::StartPersistTask(XrFutureEXT future, int64 anchor, const FString& name, UObject* outer)
|
||||
{
|
||||
if (!outer) outer = GetTransientPackage();
|
||||
UPersistTask* task = NewObject<UPersistTask>(outer);
|
||||
task->future = future;
|
||||
task->name = name;
|
||||
task->anchor = anchor;
|
||||
AddTaskToQueue(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
void UPersistTask::GetResult(bool& isSuccess, int& xrResult) const
|
||||
{
|
||||
isSuccess = result == XR_SUCCESS;
|
||||
xrResult = result;
|
||||
}
|
||||
|
||||
FString UPersistTask::GetPersistedAnchorName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
int64 UPersistTask::GetAnchor() const
|
||||
{
|
||||
return anchor;
|
||||
}
|
||||
|
||||
int UPersistTask::OnFutureComplete()
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return XR_ERROR_VALIDATION_FAILURE;
|
||||
|
||||
XrFutureCompletionEXT completion = {
|
||||
.type = XR_TYPE_FUTURE_COMPLETION_EXT,
|
||||
.next = nullptr
|
||||
};
|
||||
|
||||
XrResult ret = mod->PersistSpatialAnchorComplete(future, &completion);
|
||||
if (XR_FAILED(ret))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to persist spatial anchor: %d"), ret);
|
||||
return ret;
|
||||
}
|
||||
result = completion.futureResult;
|
||||
return ret; // No error on execution
|
||||
}
|
||||
|
||||
UFromPersistedTask::UFromPersistedTask() : anchor(0), result((int)XR_ERROR_VALIDATION_FAILURE)
|
||||
{
|
||||
}
|
||||
|
||||
UFromPersistedTask* UFromPersistedTask::StartFromPersistedTask(XrFutureEXT future, const FString& paName, const FString& aName, UObject* outer)
|
||||
{
|
||||
if (!outer) outer = GetTransientPackage();
|
||||
UFromPersistedTask* task = NewObject<UFromPersistedTask>(outer);
|
||||
task->future = future;
|
||||
task->paName = paName;
|
||||
task->aName = aName;
|
||||
AddTaskToQueue(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
void UFromPersistedTask::GetResult(int64& anchorOut, bool& isSuccess, int& xrResult) const
|
||||
{
|
||||
anchorOut = anchor;
|
||||
isSuccess = result == XR_SUCCESS;
|
||||
xrResult = result;
|
||||
}
|
||||
|
||||
FString UFromPersistedTask::GetPersistedAnchorName() const
|
||||
{
|
||||
return paName;
|
||||
}
|
||||
|
||||
FString UFromPersistedTask::GetAnchorName() const
|
||||
{
|
||||
return aName;
|
||||
}
|
||||
|
||||
int64 UFromPersistedTask::GetAnchor() const
|
||||
{
|
||||
return anchor;
|
||||
}
|
||||
|
||||
int UFromPersistedTask::OnFutureComplete()
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return XR_ERROR_VALIDATION_FAILURE;
|
||||
|
||||
XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC completion = {
|
||||
.type = XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_COMPLETION_HTC,
|
||||
.next = nullptr,
|
||||
.futureResult = XR_SUCCESS,
|
||||
.anchor = 0
|
||||
};
|
||||
|
||||
XrResult cret = mod->CreateSpatialAnchorFromPersistedAnchorComplete(future, &completion);
|
||||
if (XR_FAILED(cret) || XR_FAILED(completion.futureResult))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to create spatial anchor from persisted anchor: completeResult=%d futureResult=%d"), cret, completion.futureResult);
|
||||
return cret;
|
||||
}
|
||||
anchor = (int64)completion.anchor;
|
||||
result = completion.futureResult;
|
||||
return cret; // No error on execution
|
||||
}
|
||||
-185
@@ -1,185 +0,0 @@
|
||||
// ViveOpenXRAnchorFunctionLibrary.cpp
|
||||
// Copyright (c) 2024 HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveOpenXRAnchorFunctionLibrary.h"
|
||||
#include "ViveOpenXRAnchorPersistence.h"
|
||||
#include "ViveOpenXRAnchorAsyncTask.h"
|
||||
#include "ViveOpenXRAnchorModule.h"
|
||||
|
||||
#include "HAL/FileManager.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include <string>
|
||||
#include <inttypes.h>
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::IsAnchorSupported()
|
||||
{
|
||||
if (!FViveOpenXRAnchor::Instance()) return false;
|
||||
return FViveOpenXRAnchor::Instance()->IsSupported();
|
||||
}
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::CreateSpatialAnchor(FVector position, FRotator rotation, FString name, int64& anchor)
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
auto hmd = FViveOpenXRAnchor::HMD();
|
||||
if (!mod || !hmd) return false;
|
||||
|
||||
float w2m = hmd->GetWorldToMetersScale();
|
||||
FQuat rot = FQuat(rotation);
|
||||
XrSpatialAnchorCreateInfoHTC createInfo = mod->MakeCreateInfo(position, rot, hmd->GetTrackingSpace(), name, w2m);
|
||||
anchor = 0;
|
||||
XrSpace space;
|
||||
if (!mod->CreateSpatialAnchor(&createInfo, &space))
|
||||
return false;
|
||||
anchor = (int64)space;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::CreateSpatialAnchorT(FTransform transform, FString name, int64& anchor)
|
||||
{
|
||||
return CreateSpatialAnchor(transform.GetLocation(), transform.GetRotation().Rotator(), name, anchor);
|
||||
}
|
||||
|
||||
void UViveOpenXRAnchorFunctionLibrary::DestroySpatialAnchor(int64 anchor)
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return;
|
||||
mod->DestroySpace((XrSpace)anchor);
|
||||
}
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::LocateAnchor(int64 anchor, FTransform& transform)
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return false;
|
||||
|
||||
FVector translation;
|
||||
FRotator rotation;
|
||||
if (!mod->LocateAnchor((XrSpace)anchor, rotation, translation))
|
||||
return false;
|
||||
transform = FTransform(rotation, translation, FVector(1, 1, 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::GetSpatialAnchorName(int64 anchor, FString& name)
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return false;
|
||||
XrSpatialAnchorNameHTC xrName{};
|
||||
xrName.name[0] = 0;
|
||||
if (!mod->GetSpatialAnchorName((XrSpace)anchor, &xrName)) return false;
|
||||
// Make sure the name is null-terminated
|
||||
xrName.name[XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC - 1] = 0;
|
||||
name = FString(xrName.name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::IsPersistedAnchorSupported()
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return false;
|
||||
return mod->IsPersistenceSupported();
|
||||
}
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::AcquirePersistedAnchorCollection(UPersistedAnchorCollection*& collectionObj, UObject* outer)
|
||||
{
|
||||
collectionObj = nullptr;
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return false;
|
||||
|
||||
XrFutureEXT future = XR_NULL_HANDLE;
|
||||
XrResult result = mod->AcquirePersistedAnchorCollectionAsync(&future);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to acquire persisted anchor collection async: ret=%d"), result);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!outer) outer = GetTransientPackage();
|
||||
collectionObj = UPersistedAnchorCollection::StartAcquirePACTask(future, outer);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UViveOpenXRAnchorFunctionLibrary::ReleasePersistedAnchorCollection(UPersistedAnchorCollection* collectionObj)
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return;
|
||||
if (collectionObj == nullptr) return;
|
||||
|
||||
XrPersistedAnchorCollectionHTC collection = (XrPersistedAnchorCollectionHTC)collectionObj->GetCollection();
|
||||
if (collection == XR_NULL_HANDLE)
|
||||
{
|
||||
XrFutureEXT future = (XrFutureEXT)collectionObj->GetFuture();
|
||||
if (future != XR_NULL_HANDLE)
|
||||
collectionObj->CancelFuture();
|
||||
}
|
||||
|
||||
XrResult result = mod->ReleasePersistedAnchorCollection(collection);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to release persisted anchor collection: %p ret=%d"), collection, result);
|
||||
}
|
||||
|
||||
collectionObj->SetReleased();
|
||||
}
|
||||
|
||||
FString UViveOpenXRAnchorFunctionLibrary::GetExternalStorageDir()
|
||||
{
|
||||
FString path = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*FPaths::ProjectDir());
|
||||
//UE_LOG(ViveOXRAnchor, Log, TEXT("ProjectDir=%s"), *path);
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
if (true)
|
||||
#else
|
||||
if (false)
|
||||
#endif
|
||||
{
|
||||
FString internalPath = FPaths::Combine(TEXT("Android/data"), FApp::GetProjectName());
|
||||
//UE_LOG(ViveOXRAnchor, Log, TEXT("internalPath=%s"), *internalPath);
|
||||
if (path.Contains(internalPath)) {
|
||||
path = path.Replace(*internalPath, TEXT(""));
|
||||
path = FPaths::Combine(path, FApp::GetProjectName());
|
||||
//UE_LOG(ViveOXRAnchor, Log, TEXT("ExternalStorageDir=%s"), *path);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::GetFilesInFolder(const FString& dirPath, const FString& extName, TArray<FString>& filenames) {
|
||||
IFileManager& FileManager = IFileManager::Get();
|
||||
FileManager.FindFiles(filenames, *dirPath, *extName);
|
||||
//for (int i = 0; i < filenames.Num(); i++)
|
||||
// UE_LOG(ViveOXRAnchor, Log, TEXT("GetFilesInFolder[%d] %s"), i, *filenames[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::DeleteAnchorDirectory(const FString& dirPath)
|
||||
{
|
||||
IFileManager& FileManager = IFileManager::Get();
|
||||
return FileManager.DeleteDirectory(*dirPath);
|
||||
}
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::SaveData(const TArray<unsigned char>& data, const FString& filePathname) {
|
||||
if (FFileHelper::SaveArrayToFile(data, *filePathname)) {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Data is saved to %s"), *filePathname);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Unable to save data to %s"), *filePathname);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UViveOpenXRAnchorFunctionLibrary::LoadData(const FString& filePathname, TArray<unsigned char>& data) {
|
||||
if (FFileHelper::LoadFileToArray(data, *filePathname)) {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Load data[%d] from %s "), data.Num(), *filePathname);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Unable to load data from %s"), *filePathname);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FString UViveOpenXRAnchorFunctionLibrary::ToString_Anchor(int64 anchor) {
|
||||
return FString::Printf(TEXT("%016" PRIX64), (uint64_t)anchor);
|
||||
}
|
||||
@@ -1,460 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveOpenXRAnchorModule.h"
|
||||
#include "OpenXRCore.h"
|
||||
#include "Misc/ConfigCacheIni.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "Misc/DateTime.h"
|
||||
#include "HAL/PlatformTime.h"
|
||||
#include <string>
|
||||
|
||||
DEFINE_LOG_CATEGORY(ViveOXRAnchor);
|
||||
|
||||
FOpenXRHMD* FViveOpenXRAnchor::hmd = nullptr;
|
||||
FViveOpenXRAnchor* FViveOpenXRAnchor::instance = nullptr;
|
||||
|
||||
FOpenXRHMD* FViveOpenXRAnchor::HMD() {
|
||||
if (hmd != nullptr)
|
||||
return hmd;
|
||||
if (GEngine->XRSystem.IsValid())
|
||||
{
|
||||
hmd = static_cast<FOpenXRHMD*>(GEngine->XRSystem->GetHMDDevice());
|
||||
}
|
||||
return hmd;
|
||||
}
|
||||
|
||||
FViveOpenXRAnchor* FViveOpenXRAnchor::Instance()
|
||||
{
|
||||
if (instance != nullptr)
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GEngine->XRSystem.IsValid() && HMD() != nullptr)
|
||||
{
|
||||
for (IOpenXRExtensionPlugin* Module : HMD()->GetExtensionPlugins())
|
||||
{
|
||||
if (Module->GetDisplayName() == TEXT("ViveOpenXRAnchor"))
|
||||
{
|
||||
instance = static_cast<FViveOpenXRAnchor*>(Module);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FViveOpenXRAnchor::StartupModule()
|
||||
{
|
||||
check(GConfig && GConfig->IsReadyForUse());
|
||||
FString modeName;
|
||||
if (GConfig->GetString(TEXT("/Script/ViveOpenXRRuntimeSettings.ViveOpenXRRuntimeSettings"), TEXT("bEnableAnchor"), modeName, GEngineIni))
|
||||
{
|
||||
if (modeName.Equals("False"))
|
||||
{
|
||||
m_bEnableAnchor = false;
|
||||
}
|
||||
else if (modeName.Equals("True"))
|
||||
{
|
||||
m_bEnableAnchor = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_bEnableAnchor = false;
|
||||
|
||||
|
||||
if (m_bEnableAnchor)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Enable Anchor."));
|
||||
instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Disable Anchor."));
|
||||
instance = nullptr;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterOpenXRExtensionModularFeature();
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("StartupModule() Finished."));
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::ShutdownModule()
|
||||
{
|
||||
instance = nullptr;
|
||||
if (m_bEnableAnchor)
|
||||
UnregisterOpenXRExtensionModularFeature();
|
||||
}
|
||||
|
||||
FString FViveOpenXRAnchor::GetDisplayName()
|
||||
{
|
||||
return FString(TEXT("ViveOpenXRAnchor"));
|
||||
}
|
||||
|
||||
bool FViveOpenXRAnchor::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("GetRequiredExtensions() Add Anchor Extension Name %s."), ANSI_TO_TCHAR(XR_HTC_ANCHOR_EXTENSION_NAME));
|
||||
OutExtensions.Add(XR_HTC_ANCHOR_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FViveOpenXRAnchor::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("GetOptionalExtensions() Add Anchor Persistence Extension Name %s."), ANSI_TO_TCHAR(XR_HTC_ANCHOR_PERSISTENCE_EXTENSION_NAME));
|
||||
OutExtensions.Add(XR_HTC_ANCHOR_PERSISTENCE_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
const void* FViveOpenXRAnchor::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
||||
{
|
||||
if (!m_bEnableAnchor) return InNext;
|
||||
if (HMD() == nullptr) return InNext;
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Entry Anchor OnCreateSession."));
|
||||
|
||||
PFN_xrGetInstanceProcAddr GetIPA = xrGetInstanceProcAddr;
|
||||
bool useMockRuntime = false;
|
||||
|
||||
if (!HMD()->IsExtensionEnabled(XR_HTC_ANCHOR_EXTENSION_NAME) && !useMockRuntime)
|
||||
return InNext;
|
||||
|
||||
m_bExtAnchorEnabled = true;
|
||||
|
||||
XR_ENSURE(GetIPA(InInstance, "xrLocateSpace", (PFN_xrVoidFunction*)&xrLocateSpace));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrDestroySpace", (PFN_xrVoidFunction*)&xrDestroySpace));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrCreateSpatialAnchorHTC", (PFN_xrVoidFunction*)&xrCreateSpatialAnchorHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrGetSpatialAnchorNameHTC", (PFN_xrVoidFunction*)&xrGetSpatialAnchorNameHTC));
|
||||
|
||||
XrSystemAnchorPropertiesHTC systemAnchorProperties = {};
|
||||
systemAnchorProperties.type = XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC;
|
||||
systemAnchorProperties.next = nullptr;
|
||||
XrSystemProperties systemProperties = {};
|
||||
systemProperties.type = XR_TYPE_SYSTEM_PROPERTIES;
|
||||
systemProperties.next = &systemAnchorProperties;
|
||||
XrResult result = xrGetSystemProperties(InInstance, InSystem, &systemProperties);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("OnCreateSession() xrGetSystemProperties failed with result %d."), result);
|
||||
}
|
||||
else
|
||||
{
|
||||
isAnchorSupported = systemAnchorProperties.supportsAnchor > 0;
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("OnCreateSession() Is Anchor support: %d."), systemAnchorProperties.supportsAnchor);
|
||||
}
|
||||
if (useMockRuntime)
|
||||
isAnchorSupported = true;
|
||||
|
||||
if (!hmd->IsExtensionEnabled(XR_HTC_ANCHOR_PERSISTENCE_EXTENSION_NAME) && !useMockRuntime)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Warning, TEXT("Extension Anchor Persistence is NOT enabled."));
|
||||
return InNext;
|
||||
}
|
||||
|
||||
m_bExtAnchorPersistenceEnabled = true;
|
||||
|
||||
if (!hmd->IsExtensionEnabled(XR_EXT_FUTURE_EXTENSION_NAME) && !useMockRuntime)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Extension Future is NOT enabled. Thus, Anchor Persistence is not suported."));
|
||||
return InNext;
|
||||
}
|
||||
|
||||
auto futureMod = FViveOpenXRFuture::Instance();
|
||||
|
||||
if (futureMod == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to get Future Module."));
|
||||
return InNext;
|
||||
}
|
||||
|
||||
// Initialize new function pointers
|
||||
XR_ENSURE(GetIPA(InInstance, "xrAcquirePersistedAnchorCollectionAsyncHTC", (PFN_xrVoidFunction*)&xrAcquirePersistedAnchorCollectionAsyncHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrAcquirePersistedAnchorCollectionCompleteHTC", (PFN_xrVoidFunction*)&xrAcquirePersistedAnchorCollectionCompleteHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrReleasePersistedAnchorCollectionHTC", (PFN_xrVoidFunction*)&xrReleasePersistedAnchorCollectionHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrPersistSpatialAnchorAsyncHTC", (PFN_xrVoidFunction*)&xrPersistSpatialAnchorAsyncHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrPersistSpatialAnchorCompleteHTC", (PFN_xrVoidFunction*)&xrPersistSpatialAnchorCompleteHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrUnpersistSpatialAnchorHTC", (PFN_xrVoidFunction*)&xrUnpersistSpatialAnchorHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrEnumeratePersistedAnchorNamesHTC", (PFN_xrVoidFunction*)&xrEnumeratePersistedAnchorNamesHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrCreateSpatialAnchorFromPersistedAnchorAsyncHTC", (PFN_xrVoidFunction*)&xrCreateSpatialAnchorFromPersistedAnchorAsyncHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrCreateSpatialAnchorFromPersistedAnchorCompleteHTC", (PFN_xrVoidFunction*)&xrCreateSpatialAnchorFromPersistedAnchorCompleteHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrClearPersistedAnchorsHTC", (PFN_xrVoidFunction*)&xrClearPersistedAnchorsHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrGetPersistedAnchorPropertiesHTC", (PFN_xrVoidFunction*)&xrGetPersistedAnchorPropertiesHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrExportPersistedAnchorHTC", (PFN_xrVoidFunction*)&xrExportPersistedAnchorHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrImportPersistedAnchorHTC", (PFN_xrVoidFunction*)&xrImportPersistedAnchorHTC));
|
||||
XR_ENSURE(GetIPA(InInstance, "xrGetPersistedAnchorNameFromBufferHTC", (PFN_xrVoidFunction*)&xrGetPersistedAnchorNameFromBufferHTC));
|
||||
|
||||
isAnchorPersistenceSupported = true;
|
||||
return InNext;
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::PostCreateSession(XrSession InSession)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Entry Anchor PostCreateSession InSession %llu."), InSession);
|
||||
m_Session = InSession;
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, XrSpaceLocation* location)
|
||||
{
|
||||
if (xrLocateSpace == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("LocateSpace() xrLocateSpace is nullptr."));
|
||||
return XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
return xrLocateSpace(space, baseSpace, time, location);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::DestroySpace(XrSpace space)
|
||||
{
|
||||
if (xrDestroySpace == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("DestroySpace() xrDestroySpace is nullptr."));
|
||||
return XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
return xrDestroySpace(space);
|
||||
}
|
||||
|
||||
bool FViveOpenXRAnchor::CreateSpatialAnchor(const XrSpatialAnchorCreateInfoHTC* createInfo, XrSpace* anchor)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("CreateSpatialAnchor()"));
|
||||
if (xrCreateSpatialAnchorHTC == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("CreateSpatialAnchor() xrCreateSpatialAnchorHTC is nullptr."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (anchor == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("CreateSpatialAnchor() anchor is nullptr."));
|
||||
return false;
|
||||
}
|
||||
|
||||
*anchor = 0;
|
||||
|
||||
XrResult result = xrCreateSpatialAnchorHTC(m_Session, createInfo, anchor);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("CreateSpatialAnchor() xrCreateSpatialAnchorHTC failed with result %d."), result);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FViveOpenXRAnchor::GetSpatialAnchorName(XrSpace anchor, XrSpatialAnchorNameHTC* name)
|
||||
{
|
||||
if (xrGetSpatialAnchorNameHTC == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("GetSpatialAnchorName() xrGetSpatialAnchorNameHTC is nullptr."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name == nullptr)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("GetSpatialAnchorName() name is nullptr."));
|
||||
return false;
|
||||
}
|
||||
|
||||
name->name[0] = 0;
|
||||
|
||||
XrResult result = xrGetSpatialAnchorNameHTC(anchor, name);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("GetSpatialAnchorName() xrGetSpatialAnchorNameHTC failed with result %d."), result);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
XrSpatialAnchorCreateInfoHTC FViveOpenXRAnchor::MakeCreateInfo(const FVector& loc, const FQuat& rot, XrSpace baseSpace, FString name, float worldToMeterScale)
|
||||
{
|
||||
XrSpatialAnchorCreateInfoHTC createInfo = {};
|
||||
createInfo.type = XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC;
|
||||
createInfo.next = nullptr;
|
||||
createInfo.space = baseSpace;
|
||||
createInfo.poseInSpace = XrPosef{ ToXrQuat(rot), ToXrVector(loc, worldToMeterScale) };
|
||||
createInfo.name.name[0] = 0;
|
||||
std::string str = TCHAR_TO_ANSI(*name);
|
||||
int l = str.length();
|
||||
size_t m = l < XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC - 1 ? l : XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC - 1;
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
strncpy(createInfo.name.name, str.c_str(), m);
|
||||
#else
|
||||
strncpy_s(createInfo.name.name, str.c_str(), m);
|
||||
#endif
|
||||
createInfo.name.name[m] = 0;
|
||||
|
||||
return createInfo;
|
||||
}
|
||||
|
||||
bool FViveOpenXRAnchor::LocateAnchor(XrSpace anchor, FRotator& rotation, FVector& translation)
|
||||
{
|
||||
if (!HMD()) return false;
|
||||
if (xrLocateSpace == nullptr) return false;
|
||||
XrTime time = hmd->GetDisplayTime();
|
||||
XrSpace baseSpace = hmd->GetTrackingSpace();
|
||||
|
||||
XrSpaceLocation loc{};
|
||||
|
||||
auto result = xrLocateSpace((XrSpace)anchor, baseSpace, time, &loc);
|
||||
if (XR_FAILED(result)) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("LocateAnchor() xrLocateSpace failed. result=%d."), result);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((loc.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) == 0 ||
|
||||
(loc.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) == 0) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("LocateAnchor() locationFlags has not valid bits. Flags=%016llX"), loc.locationFlags);
|
||||
return false;
|
||||
}
|
||||
|
||||
translation = ToFVector(loc.pose.position, hmd->GetWorldToMetersScale());
|
||||
auto rot = ToFQuat(loc.pose.orientation);
|
||||
rot.Normalize();
|
||||
rotation = FRotator(rot);
|
||||
return true;
|
||||
}
|
||||
|
||||
FString FViveOpenXRAnchor::GenerateAnchorName(FString prefix) {
|
||||
uint32_t fc = GFrameCounter;
|
||||
fc %= 10000;
|
||||
uint32_t time = ((uint32_t)(FPlatformTime::Seconds() * 1000)) % 1000000;
|
||||
// This name will have a fixed length.
|
||||
FString name = prefix + FString::Printf(TEXT("_%04u_%06u_VOXR"), fc, time);
|
||||
return name;
|
||||
}
|
||||
|
||||
FString FViveOpenXRAnchor::ToFString(const XrSpatialAnchorNameHTC& xrName)
|
||||
{
|
||||
ANSICHAR name[XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC];
|
||||
#if PLATFORM_ANDROID
|
||||
strncpy(name, xrName.name, XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC - 1);
|
||||
#else
|
||||
strncpy_s(name, xrName.name, XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC - 1);
|
||||
#endif
|
||||
name[XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC - 1] = 0;
|
||||
|
||||
return FString(FUTF8ToTCHAR(name));
|
||||
}
|
||||
|
||||
XrSpatialAnchorNameHTC FViveOpenXRAnchor::FromFString(const FString& name)
|
||||
{
|
||||
XrSpatialAnchorNameHTC xrName = {};
|
||||
std::string anchorNameStr(TCHAR_TO_UTF8(*name));
|
||||
#if PLATFORM_ANDROID
|
||||
strncpy(xrName.name, anchorNameStr.c_str(), XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC - 1);
|
||||
#else
|
||||
strncpy_s(xrName.name, anchorNameStr.c_str(), XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC - 1);
|
||||
#endif
|
||||
xrName.name[XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC - 1] = 0;
|
||||
return xrName;
|
||||
}
|
||||
|
||||
XrSpatialAnchorFromPersistedAnchorCreateInfoHTC FViveOpenXRAnchor::MakeSpatialAnchorFromPersistedAnchorCreateInfo(XrPersistedAnchorCollectionHTC collection, const FString persistedAnchorName, const FString anchorName)
|
||||
{
|
||||
XrSpatialAnchorFromPersistedAnchorCreateInfoHTC createInfo = {};
|
||||
createInfo.type = XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_HTC;
|
||||
createInfo.next = nullptr;
|
||||
createInfo.persistedAnchorCollection = collection;
|
||||
createInfo.persistedAnchorName = FromFString(persistedAnchorName);
|
||||
createInfo.spatialAnchorName = FromFString(anchorName);
|
||||
return createInfo;
|
||||
}
|
||||
|
||||
// functions for the XR_HTC_anchor_persistence extension
|
||||
|
||||
XrResult FViveOpenXRAnchor::AcquirePersistedAnchorCollectionAsync(XrFutureEXT* future)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
if (future == nullptr) return XR_ERROR_VALIDATION_FAILURE;
|
||||
XrPersistedAnchorCollectionAcquireInfoHTC acquireInfo = {};
|
||||
acquireInfo.type = XR_TYPE_PERSISTED_ANCHOR_COLLECTION_ACQUIRE_INFO_HTC;
|
||||
acquireInfo.next = nullptr;
|
||||
|
||||
auto ret = xrAcquirePersistedAnchorCollectionAsyncHTC(m_Session, &acquireInfo, future);
|
||||
return ret;
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::AcquirePersistedAnchorCollectionComplete(XrFutureEXT future, XrPersistedAnchorCollectionAcquireCompletionHTC* completion)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrAcquirePersistedAnchorCollectionCompleteHTC(future, completion);
|
||||
}
|
||||
|
||||
|
||||
XrResult FViveOpenXRAnchor::ReleasePersistedAnchorCollection(XrPersistedAnchorCollectionHTC collection)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrReleasePersistedAnchorCollectionHTC(collection);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::PersistSpatialAnchorAsync(XrPersistedAnchorCollectionHTC collection, const XrSpatialAnchorPersistInfoHTC* info, XrFutureEXT* future)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrPersistSpatialAnchorAsyncHTC(collection, info, future);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::PersistSpatialAnchorComplete(XrFutureEXT future, XrFutureCompletionEXT* completion)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrPersistSpatialAnchorCompleteHTC(future, completion);
|
||||
}
|
||||
|
||||
|
||||
XrResult FViveOpenXRAnchor::UnpersistSpatialAnchor(XrPersistedAnchorCollectionHTC collection, const XrSpatialAnchorNameHTC* name)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrUnpersistSpatialAnchorHTC(collection, name);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::EnumeratePersistedAnchorNames(XrPersistedAnchorCollectionHTC collection, uint32_t nameCapacity, uint32_t* nameCount, XrSpatialAnchorNameHTC* names)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrEnumeratePersistedAnchorNamesHTC(collection, nameCapacity, nameCount, names);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::CreateSpatialAnchorFromPersistedAnchorAsync(const XrSpatialAnchorFromPersistedAnchorCreateInfoHTC* info, XrFutureEXT* future)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrCreateSpatialAnchorFromPersistedAnchorAsyncHTC(m_Session, info, future);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::CreateSpatialAnchorFromPersistedAnchorComplete(XrFutureEXT future, XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC* completion)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrCreateSpatialAnchorFromPersistedAnchorCompleteHTC(future, completion);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::ClearPersistedAnchors(XrPersistedAnchorCollectionHTC collection)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrClearPersistedAnchorsHTC(collection);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::GetPersistedAnchorProperties(XrPersistedAnchorCollectionHTC collection, XrPersistedAnchorPropertiesGetInfoHTC* properties)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrGetPersistedAnchorPropertiesHTC(collection, properties);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::ExportPersistedAnchor(XrPersistedAnchorCollectionHTC collection, const XrSpatialAnchorNameHTC* name, uint32_t dataCapacity, uint32_t* dataCount, char* data)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrExportPersistedAnchorHTC(collection, name, dataCapacity, dataCount, data);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::ImportPersistedAnchor(XrPersistedAnchorCollectionHTC collection, uint32_t dataCount, const char* data)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrImportPersistedAnchorHTC(collection, dataCount, data);
|
||||
}
|
||||
|
||||
XrResult FViveOpenXRAnchor::GetPersistedAnchorNameFromBuffer(XrPersistedAnchorCollectionHTC collection, uint32_t bufferCount, const char* buffer, XrSpatialAnchorNameHTC* name)
|
||||
{
|
||||
if (!isAnchorPersistenceSupported) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return xrGetPersistedAnchorNameFromBufferHTC(collection, bufferCount, buffer, name);
|
||||
}
|
||||
|
||||
IMPLEMENT_MODULE(FViveOpenXRAnchor, ViveOpenXRAnchor)
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Must include earlier than Unreal's OpenXR header
|
||||
#include "ViveOpenXRWrapper.h"
|
||||
#include "IOpenXRExtensionPlugin.h"
|
||||
#include "OpenXRHMD.h"
|
||||
#include "ViveOpenXRFuture.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ViveOXRAnchor, Log, All);
|
||||
|
||||
class VIVEOPENXRANCHOR_API FViveOpenXRAnchor : public IModuleInterface, public IOpenXRExtensionPlugin, public IOpenXRCustomAnchorSupport
|
||||
{
|
||||
public:
|
||||
FViveOpenXRAnchor() {}
|
||||
virtual ~FViveOpenXRAnchor() {}
|
||||
|
||||
static FOpenXRHMD* HMD();
|
||||
static FViveOpenXRAnchor* Instance();
|
||||
|
||||
/* IModuleInterface */
|
||||
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
/** IOpenXRExtensionPlugin implementation */
|
||||
|
||||
virtual FString GetDisplayName() override;
|
||||
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
virtual void PostCreateSession(XrSession InSession) override;
|
||||
|
||||
virtual IOpenXRCustomAnchorSupport* GetCustomAnchorSupport() override;
|
||||
|
||||
virtual void OnStartARSession(class UARSessionConfig* SessionConfig) override;
|
||||
|
||||
/** Stop the AR system but leave its internal state intact. */
|
||||
virtual void OnPauseARSession() override;
|
||||
|
||||
/** Stop the AR system and reset its internal state; this task must succeed. */
|
||||
virtual void OnStopARSession() override;
|
||||
|
||||
/** IOpenXRCustomAnchorSupport implementation */
|
||||
virtual bool OnPinComponent(class UARPin* Pin, XrSession InSession, XrSpace TrackingSpace, XrTime DisplayTime, float worldToMeterScale) override;
|
||||
virtual void OnRemovePin(class UARPin* Pin) override;
|
||||
virtual void OnUpdatePin(class UARPin* Pin, XrSession InSession, XrSpace TrackingSpace, XrTime DisplayTime, float worldToMeterScale) override;
|
||||
virtual bool IsLocalPinSaveSupported() const override;
|
||||
virtual bool ArePinsReadyToLoad() override;
|
||||
virtual void LoadARPins(XrSession InSession, TFunction<UARPin* (FName)> OnCreatePin) override;
|
||||
virtual bool SaveARPin(XrSession InSession, FName InName, UARPin* InPin) override;
|
||||
virtual void RemoveSavedARPin(XrSession InSession, FName InName) override;
|
||||
virtual void RemoveAllSavedARPins(XrSession InSession) override;
|
||||
|
||||
public:
|
||||
// Space functions
|
||||
XrResult LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, XrSpaceLocation* location);
|
||||
XrResult DestroySpace(XrSpace space);
|
||||
|
||||
// Extension functions
|
||||
bool CreateSpatialAnchor(const XrSpatialAnchorCreateInfoHTC* createInfo, XrSpace* anchor);
|
||||
bool GetSpatialAnchorName(XrSpace anchor, XrSpatialAnchorNameHTC* name);
|
||||
|
||||
// Persisted Anchor functions
|
||||
XrResult AcquirePersistedAnchorCollectionAsync(XrFutureEXT* future);
|
||||
XrResult AcquirePersistedAnchorCollectionComplete(XrFutureEXT future, XrPersistedAnchorCollectionAcquireCompletionHTC* completion);
|
||||
XrResult ReleasePersistedAnchorCollection(XrPersistedAnchorCollectionHTC collection);
|
||||
XrResult PersistSpatialAnchorAsync(XrPersistedAnchorCollectionHTC collection, const XrSpatialAnchorPersistInfoHTC* info, XrFutureEXT* future);
|
||||
XrResult PersistSpatialAnchorComplete(XrFutureEXT future, XrFutureCompletionEXT* completion);
|
||||
XrResult UnpersistSpatialAnchor(XrPersistedAnchorCollectionHTC collection, const XrSpatialAnchorNameHTC* name);
|
||||
XrResult EnumeratePersistedAnchorNames(XrPersistedAnchorCollectionHTC collection, uint32_t nameCapacity, uint32_t* nameCount, XrSpatialAnchorNameHTC* names);
|
||||
XrResult CreateSpatialAnchorFromPersistedAnchorAsync(const XrSpatialAnchorFromPersistedAnchorCreateInfoHTC* info, XrFutureEXT* future);
|
||||
XrResult CreateSpatialAnchorFromPersistedAnchorComplete(XrFutureEXT future, XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC* completion);
|
||||
XrResult ClearPersistedAnchors(XrPersistedAnchorCollectionHTC collection);
|
||||
XrResult GetPersistedAnchorProperties(XrPersistedAnchorCollectionHTC collection, XrPersistedAnchorPropertiesGetInfoHTC* properties);
|
||||
XrResult ExportPersistedAnchor(XrPersistedAnchorCollectionHTC collection, const XrSpatialAnchorNameHTC* name, uint32_t dataCapacity, uint32_t* dataCount, char* data);
|
||||
XrResult ImportPersistedAnchor(XrPersistedAnchorCollectionHTC collection, uint32_t dataCount, const char* data);
|
||||
XrResult GetPersistedAnchorNameFromBuffer(XrPersistedAnchorCollectionHTC collection, uint32_t bufferCount, const char* buffer, XrSpatialAnchorNameHTC* name);
|
||||
|
||||
public:
|
||||
// Helper functions
|
||||
XrSpatialAnchorCreateInfoHTC MakeCreateInfo(const FVector& InLocation, const FQuat& InRotation, XrSpace baseSpace, FString name, float worldToMeterScale);
|
||||
bool LocateAnchor(XrSpace anchor, FRotator& rotation, FVector& translation);
|
||||
inline bool IsSupported() const { return isAnchorSupported; }
|
||||
inline bool IsPersistenceSupported() const { return isAnchorPersistenceSupported; }
|
||||
inline void* GetARCollection() { return paCollectionAR; }
|
||||
|
||||
XrSpatialAnchorFromPersistedAnchorCreateInfoHTC MakeSpatialAnchorFromPersistedAnchorCreateInfo(XrPersistedAnchorCollectionHTC collection, const FString persistedAnchorName, const FString anchorName);
|
||||
|
||||
static FString GenerateAnchorName(FString prefix);
|
||||
static FString ToFString(const XrSpatialAnchorNameHTC& name);
|
||||
static XrSpatialAnchorNameHTC FromFString(const FString& name);
|
||||
|
||||
void CheckPersistedAnchorCollectionAR();
|
||||
|
||||
private:
|
||||
static FOpenXRHMD* hmd;
|
||||
static FViveOpenXRAnchor* instance;
|
||||
|
||||
bool m_bEnableAnchor = false;
|
||||
bool m_bExtAnchorEnabled = false;
|
||||
bool m_bExtAnchorPersistenceEnabled = false;
|
||||
XrSession m_Session = XR_NULL_HANDLE;
|
||||
bool isAnchorSupported = false;
|
||||
bool isAnchorPersistenceSupported = false;
|
||||
|
||||
XrPersistedAnchorCollectionHTC paCollectionAR = nullptr;
|
||||
XrFutureEXT pacFuture = nullptr;
|
||||
bool isARRequestedPersistedAnchorCollection = false;
|
||||
double timeSinceARStarted;
|
||||
|
||||
/* OpenXR Function Ptrs */
|
||||
PFN_xrLocateSpace xrLocateSpace = nullptr;
|
||||
PFN_xrDestroySpace xrDestroySpace = nullptr;
|
||||
|
||||
PFN_xrCreateSpatialAnchorHTC xrCreateSpatialAnchorHTC = nullptr;
|
||||
PFN_xrGetSpatialAnchorNameHTC xrGetSpatialAnchorNameHTC = nullptr;
|
||||
|
||||
// New function pointers for persisted anchors
|
||||
PFN_xrAcquirePersistedAnchorCollectionAsyncHTC xrAcquirePersistedAnchorCollectionAsyncHTC = nullptr;
|
||||
PFN_xrAcquirePersistedAnchorCollectionCompleteHTC xrAcquirePersistedAnchorCollectionCompleteHTC = nullptr;
|
||||
PFN_xrReleasePersistedAnchorCollectionHTC xrReleasePersistedAnchorCollectionHTC = nullptr;
|
||||
PFN_xrPersistSpatialAnchorAsyncHTC xrPersistSpatialAnchorAsyncHTC = nullptr;
|
||||
PFN_xrPersistSpatialAnchorCompleteHTC xrPersistSpatialAnchorCompleteHTC = nullptr;
|
||||
PFN_xrUnpersistSpatialAnchorHTC xrUnpersistSpatialAnchorHTC = nullptr;
|
||||
PFN_xrEnumeratePersistedAnchorNamesHTC xrEnumeratePersistedAnchorNamesHTC = nullptr;
|
||||
PFN_xrCreateSpatialAnchorFromPersistedAnchorAsyncHTC xrCreateSpatialAnchorFromPersistedAnchorAsyncHTC = nullptr;
|
||||
PFN_xrCreateSpatialAnchorFromPersistedAnchorCompleteHTC xrCreateSpatialAnchorFromPersistedAnchorCompleteHTC = nullptr;
|
||||
PFN_xrClearPersistedAnchorsHTC xrClearPersistedAnchorsHTC = nullptr;
|
||||
PFN_xrGetPersistedAnchorPropertiesHTC xrGetPersistedAnchorPropertiesHTC = nullptr;
|
||||
PFN_xrExportPersistedAnchorHTC xrExportPersistedAnchorHTC = nullptr;
|
||||
PFN_xrImportPersistedAnchorHTC xrImportPersistedAnchorHTC = nullptr;
|
||||
PFN_xrGetPersistedAnchorNameFromBufferHTC xrGetPersistedAnchorNameFromBufferHTC = nullptr;
|
||||
};
|
||||
|
||||
@@ -1,406 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveOpenXRAnchorModule.h"
|
||||
#include "OpenXRCore.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "ARBlueprintLibrary.h"
|
||||
#include "IOpenXRARModule.h"
|
||||
#include "ARPin.h"
|
||||
#include "ViveOpenXRAnchorAsyncTask.h"
|
||||
|
||||
IOpenXRCustomAnchorSupport* FViveOpenXRAnchor::GetCustomAnchorSupport()
|
||||
{
|
||||
//UE_LOG(ViveOXRAnchor, Log, TEXT("GetCustomAnchorSupport()"));
|
||||
return this;
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::OnStartARSession(class UARSessionConfig* SessionConfig) {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("OnStartARSession()"));
|
||||
timeSinceARStarted = FPlatformTime::Seconds();
|
||||
isARRequestedPersistedAnchorCollection = true;
|
||||
CheckPersistedAnchorCollectionAR();
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::OnPauseARSession() {
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::OnStopARSession() {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("OnStopARSession()"));
|
||||
isARRequestedPersistedAnchorCollection = false;
|
||||
if (paCollectionAR != nullptr) {
|
||||
ReleasePersistedAnchorCollection(paCollectionAR);
|
||||
paCollectionAR = nullptr;
|
||||
}
|
||||
timeSinceARStarted = 0;
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::CheckPersistedAnchorCollectionAR() {
|
||||
if (paCollectionAR != nullptr)
|
||||
return;
|
||||
if (!isAnchorPersistenceSupported) {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("CheckPersistedAnchorCollectionAR() Anchor persistence is not supported."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (paCollectionAR == nullptr && isARRequestedPersistedAnchorCollection) {
|
||||
if (pacFuture == nullptr) {
|
||||
auto ret = AcquirePersistedAnchorCollectionAsync(&pacFuture);
|
||||
if (XR_SUCCEEDED(ret)) {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("CheckPersistedAnchorCollectionAR() Persisted anchor collection is acquiring."));
|
||||
} else {
|
||||
pacFuture = nullptr;
|
||||
isARRequestedPersistedAnchorCollection = false;
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("CheckPersistedAnchorCollectionAR() Persisted anchor collection creation is failed."));
|
||||
}
|
||||
} else {
|
||||
bool isReady = false;
|
||||
auto futureMod = FViveOpenXRFuture::Instance();
|
||||
if (futureMod == nullptr) return;
|
||||
auto ret = futureMod->PollFuture(pacFuture, isReady);
|
||||
if (XR_FAILED(ret)) {
|
||||
pacFuture = nullptr;
|
||||
isARRequestedPersistedAnchorCollection = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReady)
|
||||
{
|
||||
XrPersistedAnchorCollectionAcquireCompletionHTC completion;
|
||||
ret = AcquirePersistedAnchorCollectionComplete(pacFuture, &completion);
|
||||
if (XR_SUCCEEDED(completion.futureResult)) {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("CheckPersistedAnchorCollectionAR() PAC=%p is acquired."), completion.persistedAnchorCollection);
|
||||
paCollectionAR = completion.persistedAnchorCollection;
|
||||
}
|
||||
else
|
||||
isARRequestedPersistedAnchorCollection = false;
|
||||
pacFuture = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FViveOpenXRAnchor::OnPinComponent(UARPin* Pin, XrSession InSession, XrSpace TrackingSpace, XrTime DisplayTime, float worldToMeterScale)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("OnPinComponent()"));
|
||||
|
||||
auto t = Pin->GetLocalToTrackingTransform();
|
||||
auto loc = t.GetLocation();
|
||||
auto rot = t.GetRotation();
|
||||
|
||||
auto debugName = Pin->GetDebugName().ToString();
|
||||
|
||||
if (debugName.IsEmpty())
|
||||
debugName = "NoName";
|
||||
// XXX We cannot create a unique anchor name and pass the created name to upper level system. Therefore its user's responsiblity to create unique name.
|
||||
// Create unique persisted anchor name
|
||||
// auto name = GenerateAnchorName(debugName);
|
||||
XrSpatialAnchorCreateInfoHTC createInfo = MakeCreateInfo(loc, rot, TrackingSpace, debugName, worldToMeterScale);
|
||||
XrSpace anchor = 0;
|
||||
if (!CreateSpatialAnchor(&createInfo, &anchor)) return false;
|
||||
Pin->SetNativeResource(anchor);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::OnRemovePin(UARPin* Pin)
|
||||
{
|
||||
XrSpace anchor = (XrSpace)Pin->GetNativeResource();
|
||||
if (xrDestroySpace == nullptr) return;
|
||||
xrDestroySpace(anchor);
|
||||
Pin->SetPinnedComponent(nullptr);
|
||||
Pin->SetNativeResource(nullptr);
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::OnUpdatePin(UARPin* Pin, XrSession InSession, XrSpace TrackingSpace, XrTime DisplayTime, float worldToMeterScale)
|
||||
{
|
||||
FRotator rotation;
|
||||
FVector translation;
|
||||
if (!LocateAnchor((XrSpace)Pin->GetNativeResource(), rotation, translation))
|
||||
return;
|
||||
Pin->OnTransformUpdated(FTransform(rotation, translation, FVector(1, 1, 1)));
|
||||
}
|
||||
|
||||
bool FViveOpenXRAnchor::IsLocalPinSaveSupported() const
|
||||
{
|
||||
//UE_LOG(ViveOXRAnchor, Log, TEXT("IsLocalPinSaveSupported()=%d"), isARRequestedPersistedAnchorCollection && isAnchorPersistenceSupported);
|
||||
return isARRequestedPersistedAnchorCollection && isAnchorPersistenceSupported;
|
||||
}
|
||||
|
||||
bool FViveOpenXRAnchor::ArePinsReadyToLoad()
|
||||
{
|
||||
// If AR session is not started, we don't need to check persisted anchor collection.
|
||||
if (!IsLocalPinSaveSupported()) return false;
|
||||
|
||||
CheckPersistedAnchorCollectionAR();
|
||||
if (paCollectionAR == nullptr) {
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("ArePinsReadyToLoad() = false"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::LoadARPins(XrSession InSession, TFunction<UARPin* (FName)> OnCreatePin)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("LoadARPins()"));
|
||||
if (!IsLocalPinSaveSupported()) return;
|
||||
|
||||
CheckPersistedAnchorCollectionAR();
|
||||
if (paCollectionAR == nullptr) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("SaveARPin() Anchor persistence is not exist. Check IsLocalPinSaveSupported first."));
|
||||
return;
|
||||
}
|
||||
|
||||
XrResult ret = XR_SUCCESS;
|
||||
uint32_t count = 0;
|
||||
TArray<XrSpatialAnchorNameHTC> AnchorNames;
|
||||
|
||||
// enumerate persisted anchors
|
||||
xrEnumeratePersistedAnchorNamesHTC(paCollectionAR, 0, &count, nullptr);
|
||||
if (count != 0) {
|
||||
AnchorNames.SetNum(count);
|
||||
xrEnumeratePersistedAnchorNamesHTC(paCollectionAR, count, &count, AnchorNames.GetData());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("LoadARPins() No Anchor name found: %d."), count);
|
||||
}
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("LoadARPins() EnumeratePersistedAnchorCount: %d."), count);
|
||||
|
||||
int created = 0;
|
||||
TArray<XrFutureEXT> list;
|
||||
// Get all PersistedAnchor from CollectionHTC
|
||||
for (auto name : AnchorNames)
|
||||
{
|
||||
FString paName = ToFString(name);
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("LoadARPins() EnumeratePersistedAnchorName: %s."), *paName);
|
||||
FString aName = paName;
|
||||
if (aName.EndsWith(TEXT("_PA")))
|
||||
{
|
||||
aName = aName.Replace(TEXT("_PA"), TEXT(""));
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("LoadARPins() Make spatial anchor: %s from persisted anchor: %s"), *aName, *paName);
|
||||
XrSpatialAnchorFromPersistedAnchorCreateInfoHTC createInfo =
|
||||
MakeSpatialAnchorFromPersistedAnchorCreateInfo(paCollectionAR, paName, aName);
|
||||
XrFutureEXT future;
|
||||
ret = CreateSpatialAnchorFromPersistedAnchorAsync(&createInfo, &future);
|
||||
if (ret == XR_SUCCESS) {
|
||||
list.Add(future);
|
||||
}
|
||||
}
|
||||
|
||||
while (!list.IsEmpty()) {
|
||||
TArray<XrFutureEXT> nextList;
|
||||
int N = list.Num();
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
auto future = list[i];
|
||||
if (future == nullptr) continue;
|
||||
bool isReady = false;
|
||||
auto futureMod = FViveOpenXRFuture::Instance();
|
||||
if (futureMod == nullptr) return;
|
||||
ret = futureMod->PollFuture(future, isReady);
|
||||
if (XR_FAILED(ret))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("LoadARPins() PollFuture failed with result %d."), ret);
|
||||
continue;
|
||||
}
|
||||
if (!isReady)
|
||||
{
|
||||
nextList.Add(future);
|
||||
continue;
|
||||
}
|
||||
|
||||
XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC completion = {
|
||||
.type = XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_COMPLETION_HTC,
|
||||
.next = nullptr,
|
||||
.futureResult = XR_SUCCESS,
|
||||
.anchor = 0
|
||||
};
|
||||
|
||||
XrResult completeResult = CreateSpatialAnchorFromPersistedAnchorComplete(future, &completion);
|
||||
if (XR_FAILED(completeResult) || XR_FAILED(completion.futureResult))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to create spatial anchor from persisted anchor: completeResult=%d futureResult=%d"), completeResult, completion.futureResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("CreateSpatialAnchorFromPersistedAnchor is success. anchor=%p. Invoke OnCreatePin."), completion.anchor);
|
||||
auto anchor = completion.anchor;
|
||||
XrSpatialAnchorNameHTC anchorName;
|
||||
FString aName;
|
||||
if (GetSpatialAnchorName(anchor, &anchorName))
|
||||
aName = ToFString(anchorName);
|
||||
// Set user defined name to ARPin
|
||||
UARPin* pin = OnCreatePin(FName(aName));
|
||||
if (pin != nullptr) {
|
||||
pin->SetNativeResource(anchor);
|
||||
created++;
|
||||
}
|
||||
}
|
||||
}
|
||||
list = nextList;
|
||||
FPlatformProcess::Sleep(0);
|
||||
}
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("LoadARPins() Created %d ARPin(s)."), created);
|
||||
}
|
||||
|
||||
bool FViveOpenXRAnchor::SaveARPin(XrSession InSession, FName InName, UARPin* InPin)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("SaveARPin()"));
|
||||
if (!IsLocalPinSaveSupported()) return false;
|
||||
|
||||
CheckPersistedAnchorCollectionAR();
|
||||
if (paCollectionAR == nullptr) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("SaveARPin() Anchor persistence is not exist. Check IsLocalPinSaveSupported first."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (InPin == nullptr || InPin->GetNativeResource() == nullptr) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("SaveARPin() Invalid ARPin."));
|
||||
return false;
|
||||
}
|
||||
|
||||
FString aName = InName.ToString();
|
||||
FString paName;
|
||||
if (!aName.EndsWith(TEXT("_PA")))
|
||||
{
|
||||
paName = aName + TEXT("_PA");
|
||||
}
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("SaveARPin() aName: %s"), *aName);
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("SaveARPin() paName: %s"), *paName);
|
||||
|
||||
// XXX We cannot create a unique anchor name and pass the created name to upper level system. Therefore its user's responsiblity to create unique name.
|
||||
// Create unique persisted anchor name
|
||||
// name = GenerateAnchorName(name);
|
||||
|
||||
XrSpatialAnchorPersistInfoHTC info = {
|
||||
XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_HTC,
|
||||
nullptr,
|
||||
(XrSpace)InPin->GetNativeResource(),
|
||||
FromFString(paName)
|
||||
};
|
||||
|
||||
XrFutureEXT future;
|
||||
auto ret = PersistSpatialAnchorAsync(paCollectionAR, &info, &future);
|
||||
|
||||
while (true) {
|
||||
bool isReady = false;
|
||||
auto futureMod = FViveOpenXRFuture::Instance();
|
||||
if (futureMod == nullptr) return false;
|
||||
ret = futureMod->PollFuture(future, isReady);
|
||||
if (XR_FAILED(ret))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("SaveARPin() PersistSpatialAnchor failed when pollFuture with result %d."), ret);
|
||||
break;
|
||||
}
|
||||
else if (isReady)
|
||||
{
|
||||
XrFutureCompletionEXT completion;
|
||||
ret = PersistSpatialAnchorComplete(future, &completion);
|
||||
if (XR_FAILED(ret)) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("SaveARPin() PersistSpatialAnchor failed when complete with result %d."), ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (XR_FAILED(completion.futureResult)) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("SaveARPin() PersistSpatialAnchor failed with result %d."), completion.futureResult);
|
||||
return false;
|
||||
}
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("SaveARPin() PersistSpatialAnchor success."));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::RemoveSavedARPin(XrSession InSession, FName InName)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("RemoveSavedARPin()"));
|
||||
if (!IsLocalPinSaveSupported()) return;
|
||||
|
||||
CheckPersistedAnchorCollectionAR();
|
||||
if (paCollectionAR == nullptr) {
|
||||
// If supported, when ARSession started, it should be created. Developer Should check IsLocalPinSaveSupported first.
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("SaveARPin() Anchor persistence is not exist. Check IsLocalPinSaveSupported first."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Add ARPin marker
|
||||
FString aName = InName.ToString();
|
||||
FString paName = aName;
|
||||
if (!paName.EndsWith(TEXT("_PA")))
|
||||
{
|
||||
paName += TEXT("_PA");
|
||||
}
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Remove : %s from Local"), *aName);
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Remove : %s from Collection"), *paName);
|
||||
|
||||
XrSpatialAnchorNameHTC xrName = FromFString(paName);
|
||||
|
||||
// Remove SpatialAnchor from collectionHTC
|
||||
auto ret = UnpersistSpatialAnchor(paCollectionAR, &xrName);
|
||||
if (ret != XR_SUCCESS) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("RemoveSavedARPin() UnpersistSpatialAnchor failed with result %d."), ret);
|
||||
}
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Check collection removed"));
|
||||
uint32_t nameCount = 0;
|
||||
XrSpatialAnchorNameHTC* xrNames = new XrSpatialAnchorNameHTC[nameCount];
|
||||
FMemory::Memset(xrNames, 0, sizeof(XrSpatialAnchorNameHTC) * nameCount);
|
||||
XrResult result = EnumeratePersistedAnchorNames((XrPersistedAnchorCollectionHTC)paCollectionAR, nameCount, &nameCount, xrNames);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to enumerate persisted anchor names: %d"), result);
|
||||
delete[] xrNames;
|
||||
}
|
||||
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Find: %d in collection"), nameCount);
|
||||
for (uint32_t i = 0; i < nameCount; i++)
|
||||
{
|
||||
FString anchorName = ToFString(xrNames[i]);
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("EnumeratePersistedAnchorNames: %s"), *anchorName);
|
||||
}
|
||||
}
|
||||
|
||||
void FViveOpenXRAnchor::RemoveAllSavedARPins(XrSession InSession)
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("RemoveAllSavedARPins()"));
|
||||
if (!IsLocalPinSaveSupported()) return;
|
||||
|
||||
CheckPersistedAnchorCollectionAR();
|
||||
if (paCollectionAR == nullptr) {
|
||||
// If supported, when ARSession started, it should be created. Developer Should check IsLocalPinSaveSupported first.
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("SaveARPin() Anchor persistence is not exist. Check IsLocalPinSaveSupported first."));
|
||||
return;
|
||||
}
|
||||
auto ret = ClearPersistedAnchors(paCollectionAR);
|
||||
if (ret != XR_SUCCESS) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("RemoveAllSavedARPins() ClearPersistedAnchors failed with result %d."), ret);
|
||||
}
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Collection :%p"), paCollectionAR);
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Check collection all removed"));
|
||||
|
||||
// Check collection removed
|
||||
uint32_t nameCount = 0;
|
||||
XrSpatialAnchorNameHTC* xrNames = new XrSpatialAnchorNameHTC[nameCount];
|
||||
FMemory::Memset(xrNames, 0, sizeof(XrSpatialAnchorNameHTC) * nameCount);
|
||||
XrResult result = EnumeratePersistedAnchorNames((XrPersistedAnchorCollectionHTC)paCollectionAR, nameCount, &nameCount, xrNames);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to enumerate persisted anchor names: %d"), result);
|
||||
delete[] xrNames;
|
||||
}
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("Find: %d in collection"), nameCount);
|
||||
for (uint32_t i = 0; i < nameCount; i++)
|
||||
{
|
||||
FString name = ToFString(xrNames[i]);
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("EnumeratePersistedAnchorNames: %s"), *name);
|
||||
}
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
// ViveOpenXRAnchorFunctionLibrary.cpp
|
||||
// Copyright (c) 2024 HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveOpenXRAnchorPersistence.h"
|
||||
#include "ViveOpenXRAnchorModule.h"
|
||||
#include "ViveOpenXRAnchorAsyncTask.h"
|
||||
#include "ViveOpenXRFuture.h"
|
||||
#include "ViveOpenXRAnchorAsyncTask.h"
|
||||
|
||||
UPersistedAnchorCollection::UPersistedAnchorCollection() : collection(XR_NULL_HANDLE) {}
|
||||
|
||||
UPersistedAnchorCollection* UPersistedAnchorCollection::StartAcquirePACTask(XrFutureEXT future, UObject* outer)
|
||||
{
|
||||
if (!outer)
|
||||
outer = GetTransientPackage();
|
||||
UPersistedAnchorCollection* task = NewObject<UPersistedAnchorCollection>(outer);
|
||||
task->future = future;
|
||||
AddTaskToQueue(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
// After acquired, the task will be added to the task queue. And the OnFutureComplete will be called when the future is ready.
|
||||
int UPersistedAnchorCollection::OnFutureComplete()
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("UPersistedAnchorCollection::OnFutureComplete"));
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return XR_ERROR_INSTANCE_LOST;
|
||||
|
||||
XrPersistedAnchorCollectionAcquireCompletionHTC completion = {
|
||||
.type = XR_TYPE_PERSISTED_ANCHOR_COLLECTION_ACQUIRE_COMPLETION_HTC,
|
||||
.next = nullptr,
|
||||
.futureResult = XR_SUCCESS,
|
||||
.persistedAnchorCollection = nullptr
|
||||
};
|
||||
|
||||
collection = FViveOpenXRAnchor::Instance()->GetARCollection();
|
||||
UE_LOG(ViveOXRAnchor, Log, TEXT("AcquirePersistedAnchorCollectionComplete PAC=%p"), collection);
|
||||
return 0; // No error on execution
|
||||
}
|
||||
|
||||
bool UPersistedAnchorCollection::IsInitialized() const {
|
||||
return IsDone();
|
||||
}
|
||||
|
||||
bool UPersistedAnchorCollection::IsAvailable() const {
|
||||
// Must check IsInitialized first. Therefore if collection is null, not available.
|
||||
return collection != XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
bool UPersistedAnchorCollection::CheckPAC()
|
||||
{
|
||||
if (IsDone())
|
||||
return IsAvailable();
|
||||
|
||||
WaitTask();
|
||||
return IsAvailable();
|
||||
}
|
||||
|
||||
|
||||
UPersistTask* UPersistedAnchorCollection::PersistSpatialAnchor(int64 anchor, const FString& name)
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return nullptr;
|
||||
if (!CheckPAC()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
XrSpatialAnchorPersistInfoHTC persistInfo = {};
|
||||
persistInfo.type = XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_HTC;
|
||||
persistInfo.anchor = (XrSpace)anchor;
|
||||
persistInfo.persistedAnchorName = mod->FromFString(name);
|
||||
XrFutureEXT futureLocal;
|
||||
XrResult result = mod->PersistSpatialAnchorAsync((XrPersistedAnchorCollectionHTC)collection, &persistInfo, &futureLocal);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to persist spatial anchor: %d"), result);
|
||||
}
|
||||
|
||||
return UPersistTask::StartPersistTask(futureLocal, anchor, name, this);
|
||||
}
|
||||
|
||||
bool UPersistedAnchorCollection::UnpersistSpatialAnchor(const FString& name)
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return false;
|
||||
if (!CheckPAC()) return false;
|
||||
|
||||
XrSpatialAnchorNameHTC anchorName = mod->FromFString(name);
|
||||
|
||||
XrResult result = mod->UnpersistSpatialAnchor((XrPersistedAnchorCollectionHTC)collection, &anchorName);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to unpersist spatial anchor: %d"), result);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UPersistedAnchorCollection::EnumeratePersistedAnchorNames(TArray<FString>& anchorNames)
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return false;
|
||||
if (!CheckPAC()) return false;
|
||||
|
||||
uint32_t nameCount = 0;
|
||||
XrResult result = mod->EnumeratePersistedAnchorNames((XrPersistedAnchorCollectionHTC)collection, 0, &nameCount, nullptr);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to enumerate persisted anchor names 1: %d"), result);
|
||||
return false;
|
||||
}
|
||||
|
||||
anchorNames.SetNum(0);
|
||||
if (nameCount == 0)
|
||||
return true;
|
||||
|
||||
XrSpatialAnchorNameHTC* xrNames = new XrSpatialAnchorNameHTC[nameCount];
|
||||
FMemory::Memset(xrNames, 0, sizeof(XrSpatialAnchorNameHTC) * nameCount);
|
||||
result = mod->EnumeratePersistedAnchorNames((XrPersistedAnchorCollectionHTC)collection, nameCount, &nameCount, xrNames);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to enumerate persisted anchor names 2: %d"), result);
|
||||
delete[] xrNames;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < nameCount; i++)
|
||||
{
|
||||
FString name = mod->ToFString(xrNames[i]);
|
||||
//UE_LOG(ViveOXRAnchor, Log, TEXT("EnumeratePersistedAnchorNames: %s"), *name);
|
||||
anchorNames.Add(name);
|
||||
}
|
||||
|
||||
delete[] xrNames;
|
||||
return true;
|
||||
}
|
||||
|
||||
UFromPersistedTask* UPersistedAnchorCollection::CreateSpatialAnchorFromPersistedAnchor(const FString& persistedAnchorName, const FString& anchorName)
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return nullptr;
|
||||
if (!CheckPAC()) return nullptr;
|
||||
|
||||
XrSpatialAnchorFromPersistedAnchorCreateInfoHTC createInfo = {};
|
||||
createInfo.type = XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_HTC;
|
||||
createInfo.persistedAnchorCollection = (XrPersistedAnchorCollectionHTC)collection;
|
||||
createInfo.persistedAnchorName = mod->FromFString(persistedAnchorName);
|
||||
createInfo.spatialAnchorName = mod->FromFString(anchorName);
|
||||
|
||||
XrFutureEXT futureLocal;
|
||||
XrResult result = mod->CreateSpatialAnchorFromPersistedAnchorAsync(&createInfo, &futureLocal);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to create spatial anchor from persisted anchor: %d"), result);
|
||||
}
|
||||
|
||||
return UFromPersistedTask::StartFromPersistedTask(futureLocal, persistedAnchorName, anchorName, this);
|
||||
}
|
||||
|
||||
bool UPersistedAnchorCollection::ClearPersistedAnchors()
|
||||
{
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return false;
|
||||
if (!CheckPAC()) return false;
|
||||
|
||||
XrResult result = mod->ClearPersistedAnchors((XrPersistedAnchorCollectionHTC)collection);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("Failed to clear persisted anchors: %d"), result);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UExportTask* UPersistedAnchorCollection::ExportPersistedAnchor(const FString& name, const FString& folder)
|
||||
{
|
||||
return UExportTask::StartExportTask(this, name, this, folder);
|
||||
}
|
||||
|
||||
UImportTask* UPersistedAnchorCollection::ImportPersistedAnchor(const FString& taskName, const TArray<uint8>& data, const FString& filePath)
|
||||
{
|
||||
auto task = UImportTask::StartImportTask(this, data, this, filePath);
|
||||
task->SetTaskName(taskName);
|
||||
return task;
|
||||
}
|
||||
|
||||
bool UPersistedAnchorCollection::GetPersistedAnchorNameFromBuffer(const TArray<uint8>& Data, FString& name)
|
||||
{
|
||||
name = "";
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return false;
|
||||
if (!CheckPAC()) return false;
|
||||
|
||||
XrSpatialAnchorNameHTC xrName{};
|
||||
auto ret = mod->GetPersistedAnchorNameFromBuffer((XrPersistedAnchorCollectionHTC)collection, Data.Num(), (char*)Data.GetData(), &xrName);
|
||||
if (XR_FAILED(ret)) {
|
||||
UE_LOG(ViveOXRAnchor, Error, TEXT("GetPersistedAnchorNameFromBuffer return fail: %d"), ret);
|
||||
return false;
|
||||
}
|
||||
name = FViveOpenXRAnchor::ToFString(xrName);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UPersistedAnchorCollection::GetMaxPersistedAnchorCount(int& maxCount)
|
||||
{
|
||||
maxCount = 0;
|
||||
auto mod = FViveOpenXRAnchor::Instance();
|
||||
if (!mod) return;
|
||||
if (!CheckPAC()) return;
|
||||
|
||||
XrPersistedAnchorPropertiesGetInfoHTC info{};
|
||||
info.type = XR_TYPE_PERSISTED_ANCHOR_PROPERTIES_GET_INFO_HTC;
|
||||
info.next = nullptr;
|
||||
info.maxPersistedAnchorCount = 0;
|
||||
auto ret = mod->GetPersistedAnchorProperties((XrPersistedAnchorCollectionHTC)collection, &info);
|
||||
if (ret == XR_SUCCESS)
|
||||
maxCount = (int)info.maxPersistedAnchorCount;
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ARPin.h"
|
||||
#include "Kismet/BlueprintAsyncActionBase.h"
|
||||
#include "ViveOpenXRAnchorAsyncTask.h"
|
||||
#include "ViveOpenXRAnchorAsyncAction.generated.h"
|
||||
|
||||
//DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FExportCompleteOutputPin, const FString&, filePath);
|
||||
|
||||
/**
|
||||
* @param folderPath The full path of the result exported folder.
|
||||
*/
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FExportAllCompleteOutputPin, const FString&, folderPath);
|
||||
|
||||
//DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FImportCompleteOutputPin, UARPin*, AR_pin);
|
||||
|
||||
/**
|
||||
* @param AR_pins All AR Pins imported from the folder.
|
||||
*/
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FImportAllCompleteOutputPin, const TArray<UARPin*>&, AR_pins);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FExportImportFailedOutputPin);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
//UCLASS()
|
||||
//class VIVEOPENXRANCHOR_API UViveOpenXRAnchorExportAsyncAction : public UBlueprintAsyncActionBase, public FTickableGameObject
|
||||
//{
|
||||
// GENERATED_BODY()
|
||||
//
|
||||
//public:
|
||||
// UPROPERTY(BlueprintAssignable)
|
||||
// FExportCompleteOutputPin Complete;
|
||||
//
|
||||
// UPROPERTY(BlueprintAssignable)
|
||||
// FExportImportFailedOutputPin Failure;
|
||||
//
|
||||
//private:
|
||||
// UObject* WorldContextObject;
|
||||
// UARPin* mARPin = nullptr;
|
||||
// FString mFolder = "";
|
||||
// FString mFileName = "";
|
||||
// UExportTask* task = nullptr;
|
||||
//
|
||||
//public:
|
||||
// UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Export Saved ARPin"), Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
// static UViveOpenXRAnchorExportAsyncAction* ExportPersistedARPin(UObject* WorldContextObject, UARPin* AR_Pin, const FString folderName = "ARPins");
|
||||
//
|
||||
// // UBlueprintAsyncActionBase interface
|
||||
// virtual void Activate() override;
|
||||
// virtual void Tick(float DeltaTime) override {};
|
||||
//
|
||||
// TStatId GetStatId() const override { return TStatId(); }
|
||||
//private:
|
||||
//
|
||||
//};
|
||||
|
||||
/**
|
||||
* Expory All Saved AR Pins to external storage folder.
|
||||
*/
|
||||
UCLASS()
|
||||
class VIVEOPENXRANCHOR_API UViveOpenXRAnchorExportAllAsyncAction : public UBlueprintAsyncActionBase, public FTickableGameObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FExportAllCompleteOutputPin Complete;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FExportImportFailedOutputPin Failure;
|
||||
|
||||
private:
|
||||
UObject* WorldContextObject;
|
||||
FString mFolder = "";
|
||||
TArray<UExportTask*> tasks;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Export All Saved AR Pins to external storage folder.
|
||||
* @param folderName The folder name to store exported AR Pins.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Export All Saved ARPins"), Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
static UViveOpenXRAnchorExportAllAsyncAction* ExportAllPersistedARPins(UObject* WorldContextObject, const FString folderName = "ARPins");
|
||||
|
||||
// UBlueprintAsyncActionBase interface
|
||||
virtual void Activate() override;
|
||||
virtual void Tick(float DeltaTime) override {};
|
||||
|
||||
TStatId GetStatId() const override { return TStatId(); }
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
//UCLASS()
|
||||
//class VIVEOPENXRANCHOR_API UViveOpenXRAnchorImportAsyncAction : public UBlueprintAsyncActionBase, public FTickableGameObject
|
||||
//{
|
||||
// GENERATED_BODY()
|
||||
//
|
||||
//public:
|
||||
// UPROPERTY(BlueprintAssignable)
|
||||
// FImportCompleteOutputPin Complete;
|
||||
//
|
||||
// UPROPERTY(BlueprintAssignable)
|
||||
// FExportImportFailedOutputPin Failure;
|
||||
//
|
||||
//private:
|
||||
// UObject* WorldContextObject;
|
||||
// FString mFilepath = "";
|
||||
// TArray<uint8> mData;
|
||||
// UImportTask* task = nullptr;
|
||||
// FString anchorName;
|
||||
//
|
||||
//public:
|
||||
// UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Import ARPin from File"), Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
// static UViveOpenXRAnchorImportAsyncAction* ImportPersistedARPinFromFile(UObject* WorldContextObject, const FString filePath);
|
||||
//
|
||||
// //UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Import ARPin from Data"), Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
// //static UViveOpenXRAnchorImportAsyncAction* ImportPersistedARPinFromData(UObject* WorldContextObject, const TArray<uint8>& data);
|
||||
//
|
||||
// // UBlueprintAsyncActionBase interface
|
||||
// virtual void Activate() override;
|
||||
// virtual void Tick(float DeltaTime) override {};
|
||||
//
|
||||
// //virtual void Tick(float DeltaTime) override;
|
||||
// TStatId GetStatId() const override { return TStatId(); }
|
||||
//private:
|
||||
//
|
||||
//};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class VIVEOPENXRANCHOR_API UViveOpenXRAnchorImportAllAsyncAction : public UBlueprintAsyncActionBase, public FTickableGameObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FImportAllCompleteOutputPin Complete;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FExportImportFailedOutputPin Failure;
|
||||
|
||||
private:
|
||||
UObject* WorldContextObject;
|
||||
FString mFolderpath = "";
|
||||
TArray<UImportTask*> tasks;
|
||||
TArray<UARPin*> pins;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Import All Saved AR Pins from external storage folder.
|
||||
* @param folderPath The full path or relative path to external storage where contains the Persisted Anchor files.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Import ARPins from Folder"), Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
static UViveOpenXRAnchorImportAllAsyncAction* ImportAllPersistedARPinsFromFolder(UObject* WorldContextObject, const FString folderPath);
|
||||
|
||||
|
||||
// UBlueprintAsyncActionBase interface
|
||||
virtual void Activate() override;
|
||||
virtual void Tick(float DeltaTime) override {};
|
||||
|
||||
TStatId GetStatId() const override { return TStatId(); }
|
||||
private:
|
||||
|
||||
};
|
||||
@@ -1,175 +0,0 @@
|
||||
// ViveOpenXRAnchorFunctionLibrary.h
|
||||
// Copyright (c) 2024 HTC Corporation. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "Delegates/DelegateCombinations.h"
|
||||
#include "HAL/CriticalSection.h"
|
||||
#include "ViveOpenXRAsyncTask.h"
|
||||
#include "ViveOpenXRFuture.h"
|
||||
#include "ViveOpenXRFutureAsyncTask.h"
|
||||
#include "ViveOpenXRAnchorAsyncTask.generated.h"
|
||||
|
||||
class UPersistedAnchorCollection;
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class VIVEOPENXRANCHOR_API UPersistTask : public UFutureTask
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPersistTask();
|
||||
virtual ~UPersistTask() {}
|
||||
|
||||
static UPersistTask* StartPersistTask(XrFutureEXT future, int64 anchor, const FString& name, UObject* outer);
|
||||
|
||||
/**
|
||||
* When persist a spatial anchor, it might take a while.
|
||||
* Use the task to get the result if the anchor is really persisted.
|
||||
* The result is not reliable if task is not completed or has error.
|
||||
* @param isSuccess True if the persisted anchor is persisted successfully.
|
||||
* @param xrResult The openxr's result code.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
void GetResult(bool& isSuccess, int& xrResult) const;
|
||||
|
||||
// The name of anchor persisted. You can use this anchor as this task's identity.
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
FString GetPersistedAnchorName() const;
|
||||
|
||||
// The anchor persisted. You can use this anchor as this task's identity.
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
int64 GetAnchor() const;
|
||||
|
||||
protected:
|
||||
virtual int OnFutureComplete() override;
|
||||
|
||||
private:
|
||||
int64 anchor;
|
||||
FString name;
|
||||
int32 result;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class VIVEOPENXRANCHOR_API UFromPersistedTask : public UFutureTask
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFromPersistedTask();
|
||||
virtual ~UFromPersistedTask() {}
|
||||
|
||||
static UFromPersistedTask* StartFromPersistedTask(XrFutureEXT future, const FString& paName, const FString& aName, UObject* outer);
|
||||
|
||||
/**
|
||||
* When create a spatial anchor from persisted anchor, it might take a while.
|
||||
* Use the task to check if the persisted anchor can be really found and be located.
|
||||
* The result is not reliable if task is not completed or has error.
|
||||
* @param anchor the created spatial anchor
|
||||
* @param isSuccess True if the persisted anchor is found and located.
|
||||
* @param xrResult The openxr's result code.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
void GetResult(int64& anchor, bool& isSuccess, int& xrResult) const;
|
||||
|
||||
// The persisted anchor's name used to create spatial anchor.
|
||||
// You can use the name as this task's identity.
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
FString GetPersistedAnchorName() const;
|
||||
|
||||
// The name used to create spatial anchor.
|
||||
// You can use the name as this task's identity.
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
FString GetAnchorName() const;
|
||||
|
||||
// The created anchor. it is the reasult of this task.
|
||||
// The result is not reliable if task is not completed or has error.
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
int64 GetAnchor() const;
|
||||
|
||||
protected:
|
||||
virtual int OnFutureComplete() override;
|
||||
|
||||
private:
|
||||
int64 anchor;
|
||||
FString paName;
|
||||
FString aName;
|
||||
int32 result;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class VIVEOPENXRANCHOR_API UExportTask : public UBaseTask
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UExportTask();
|
||||
|
||||
static UExportTask* StartExportTask(UPersistedAnchorCollection* collection, const FString& name, UObject* outer, const FString& folderName);
|
||||
|
||||
/**
|
||||
* Exported persisted anchor's data. Should only get result iftask is completed.
|
||||
* Because the result will update by another thread, use impure function let you known the result may change over time.
|
||||
* @param data The exported persisted anchor data.
|
||||
* @param isSuccess True if the persisted anchor is exported successfully.
|
||||
* @param xrResult The openxr's result code.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
void GetResult(TArray<uint8>& data, bool& isSuccess, int& xrResult) const;
|
||||
|
||||
// The exported persisted anchor name
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
FString GetPersistedAnchorName() const;
|
||||
|
||||
protected:
|
||||
virtual void ExecuteTask() override;
|
||||
|
||||
private:
|
||||
UPersistedAnchorCollection* collectionObj;
|
||||
FString folderPath;
|
||||
FString persistedAnchorName;
|
||||
int32 result;
|
||||
TArray<uint8> data;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class VIVEOPENXRANCHOR_API UImportTask : public UBaseTask
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UImportTask();
|
||||
|
||||
static UImportTask* StartImportTask(UPersistedAnchorCollection* collection, const TArray<uint8>& data, UObject* outer, const FString& filePath);
|
||||
|
||||
/**
|
||||
* Task name is optinal. Use it to manager yuour tasks.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
void SetTaskName(const FString& name);
|
||||
|
||||
/**
|
||||
* Task name is optinal. Use it to manager yuour tasks.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
FString GetTaskName() const;
|
||||
|
||||
/**
|
||||
* Check if import is successfuly. Should only get result if task is completed.
|
||||
* @param isSuccess True if the persisted anchor is exported successfully.
|
||||
* @param xrResult The openxr's result code.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
void GetResult(bool& isSuccess, int& xrResult) const;
|
||||
|
||||
protected:
|
||||
virtual void ExecuteTask() override;
|
||||
|
||||
UPersistedAnchorCollection* collectionObj;
|
||||
FString filePath;
|
||||
int32 result;
|
||||
FString taskName;
|
||||
TArray<uint8> data;
|
||||
};
|
||||
@@ -1,109 +0,0 @@
|
||||
// ViveOpenXRAnchorFunctionLibrary.h
|
||||
// Copyright (c) 2024 HTC Corporation. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "ViveOpenXRAnchorFunctionLibrary.generated.h"
|
||||
|
||||
/**
|
||||
* Blueprint function library for ViveOpenXR HTC Anchor extension.
|
||||
*/
|
||||
UCLASS(ClassGroup = OpenXR)
|
||||
class VIVEOPENXRANCHOR_API UViveOpenXRAnchorFunctionLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor",
|
||||
meta = (ToolTip = "Check if Anchor is supported by requesting SystemProperties."))
|
||||
static bool IsAnchorSupported();
|
||||
|
||||
/**
|
||||
* Creates a spatial anchor with pose in the tracking space.
|
||||
* The anchor is represented by an XrSpace. In Unreal, anchor is in the form of int64.
|
||||
* And its pose can be tracked via xrLocateSpace. Once the anchor is no longer needed,
|
||||
* call DestroySpatialAnchor to erase the anchor.
|
||||
* @param position The position of the anchor in VR's trakcing space.
|
||||
* @param rotation The rotation of the anchor in VR's trakcing space.
|
||||
* @param name The name of the anchor. This name is used to identify the anchor. Should b a unique name.
|
||||
*/
|
||||
UFUNCTION(/*BlueprintCallable,*/ Category = "ViveOpenXR|SpatialAnchor")
|
||||
static bool CreateSpatialAnchor(FVector position, FRotator rotation, FString name, int64& anchor);
|
||||
|
||||
UFUNCTION(/*BlueprintCallable,*/ Category = "ViveOpenXR|SpatialAnchor",
|
||||
meta = (ToolTip = "Similar to CreateSpatialAnchor, and take a transform for input. Transform's scale is ignored."))
|
||||
static bool CreateSpatialAnchorT(FTransform transform, FString name, int64& anchor);
|
||||
|
||||
UFUNCTION(/*BlueprintCallable,*/ Category = "ViveOpenXR|SpatialAnchor",
|
||||
meta = (ToolTip = "Call xrDestroySpace to erase the anchor."))
|
||||
static void DestroySpatialAnchor(int64 anchor);
|
||||
|
||||
UFUNCTION(/*BlueprintCallable,*/ Category = "ViveOpenXR|SpatialAnchor",
|
||||
meta = (ToolTip = "Call xrLocateSpace to get the anchor's current pose based on tracking origin setting."))
|
||||
static bool LocateAnchor(int64 anchor, FTransform& transform);
|
||||
|
||||
UFUNCTION(/*BlueprintCallable,*/ Category = "ViveOpenXR|SpatialAnchor",
|
||||
meta = (ToolTip = "Gets the name given to the anchor when it was created."))
|
||||
static bool GetSpatialAnchorName(int64 anchor, FString& name);
|
||||
|
||||
// Persistence Anchor Functions
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence",
|
||||
meta = (ToolTip = "Check if Persisted Anchor is supported."))
|
||||
static bool IsPersistedAnchorSupported();
|
||||
|
||||
/**
|
||||
* Acquires a persisted anchor collection. Use this collection object to perform all persisted anchor operations.
|
||||
* After acquire, you still need wait the initialization done. Check IsInitialized() to know if it's ready.
|
||||
* Please manually call ReleasePersistedAnchorCollection to release the collection object.
|
||||
* @param collection The acquired collection object.
|
||||
* @param outer The owner of the collection object. If owner is destroyed, the collection object will be forced to release. Can be null if you can manager the life cycle well.
|
||||
* @return True if the collection object is valid.
|
||||
*/
|
||||
UFUNCTION(/*BlueprintCallable,*/ Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
static bool AcquirePersistedAnchorCollection(UPersistedAnchorCollection*& collection, UObject* outer);
|
||||
|
||||
UFUNCTION(/*BlueprintCallable,*/ Category = "ViveOpenXR|SpatialAnchor|Persistence",
|
||||
meta = (ToolTip = "Release the persisted anchor collection."))
|
||||
static void ReleasePersistedAnchorCollection(UPersistedAnchorCollection* collection);
|
||||
|
||||
|
||||
public:
|
||||
// Helper functions
|
||||
|
||||
/**
|
||||
* Helper function to get the external storage path.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
static FString GetExternalStorageDir();
|
||||
|
||||
/**
|
||||
* Helper function to list all filenames with the extension in the folder.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
static bool GetFilesInFolder(const FString& dirPath, const FString& extName, TArray<FString>& filenames);
|
||||
|
||||
/**
|
||||
* Helper function to force delete directory.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
static bool DeleteAnchorDirectory(const FString& dirPath);
|
||||
|
||||
/**
|
||||
* Helper function to save a persisted spatial anchor data to file.
|
||||
*/
|
||||
UFUNCTION(/*BlueprintCallable,*/ Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
static bool SaveData(const TArray<uint8>& data, const FString& filePathname);
|
||||
|
||||
/**
|
||||
* Helper function to load a persisted spatial anchor data from file.
|
||||
*/
|
||||
UFUNCTION(/*BlueprintCallable,*/ Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
static bool LoadData(const FString& filePathname, TArray<uint8>& data);
|
||||
|
||||
/** Returns Hex string of anchor */
|
||||
UFUNCTION(/*BlueprintCallable, BlueprintPure,*/ Category = "ViveOpenXR|SpatialAnchor|Persistence",
|
||||
meta = (DisplayName = "ToString (anchor)"))
|
||||
static FString ToString_Anchor(int64 anchor);
|
||||
};
|
||||
@@ -1,121 +0,0 @@
|
||||
// ViveOpenXRAnchorFunctionLibrary.h
|
||||
// Copyright (c) 2024 HTC Corporation. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "ViveOpenXRFuture.h"
|
||||
#include "ViveOpenXRAnchorAsyncTask.h"
|
||||
#include "ViveOpenXRAnchorPersistence.generated.h"
|
||||
|
||||
/**
|
||||
* Blueprint function library for ViveOpenXR HTC Anchor Persistence extension.
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class VIVEOPENXRANCHOR_API UPersistedAnchorCollection : public UFutureTask
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPersistedAnchorCollection();
|
||||
|
||||
static UPersistedAnchorCollection* StartAcquirePACTask(XrFutureEXT future, UObject* outer);
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence",
|
||||
meta = (ToolTip = "Check if the creation of PersistedAnchorCollection is done."))
|
||||
bool IsInitialized() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence",
|
||||
meta = (ToolTip = "If IsInitialized, return if PAC is available. If not IsInitialized, always return false."))
|
||||
bool IsAvailable() const;
|
||||
|
||||
/**
|
||||
* Persist a spatial anchor with a unique name.
|
||||
* If PersistedAnchorCollection is not initialized block until it is initialized.
|
||||
* @param anchor The spatial anchor you want to persist.
|
||||
* @param name The new name for the persisted anchor. Every persisted anchor should have a unique name.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
UPersistTask* PersistSpatialAnchor(int64 anchor, const FString& name);
|
||||
|
||||
/**
|
||||
* Unpersist a spatial anchor with the persisted anchor's name.
|
||||
* If PersistedAnchorCollection is not initialized block until it is initialized.
|
||||
* @param name The name of the persisted anchor you want to unpersist.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
bool UnpersistSpatialAnchor(const FString& name);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence",
|
||||
meta = (ToolTip = "Enumerate the names of persisted anchors. Need PersistedAnchorCollection be available."))
|
||||
bool EnumeratePersistedAnchorNames(TArray<FString>& anchorNames);
|
||||
|
||||
/**
|
||||
* Create a spatial anchor from a persisted anchor.
|
||||
* If PersistedAnchorCollection is not initialized block until it is initialized.
|
||||
* @param pAnchorName The name of the persisted anchor you want to create from.
|
||||
* @param anchorName The name of the new spatial anchor.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
UFromPersistedTask* CreateSpatialAnchorFromPersistedAnchor(const FString& pAnchorName, const FString& anchorName);
|
||||
|
||||
/**
|
||||
* Clear all persisted anchors.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
bool ClearPersistedAnchors();
|
||||
|
||||
/**
|
||||
* Export the persisted anchor's data. This data could be imported by another device. This is an asynchronized function.
|
||||
* Use the returned task to get result.
|
||||
* If PersistedAnchorCollection is not initialized block until it is initialized.
|
||||
* @param name The name of the persisted anchor you want to export.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
UExportTask* ExportPersistedAnchor(const FString& name, const FString& folder);
|
||||
|
||||
/**
|
||||
* Import the persisted anchor's data. The data could be previous saved or sent by another device.
|
||||
* This is an asynchronized function. Use the returend task to get result.
|
||||
* If PersistedAnchorCollection is not initialized block until it is initialized.
|
||||
* @param taskName taskName are optional. You can use it to named this task for your own management.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
UImportTask* ImportPersistedAnchor(const FString& taskName, const TArray<uint8>& Data, const FString& filePath);
|
||||
|
||||
/**
|
||||
* The exported data include a persisted anchor name. After Import, the name will be in the name array
|
||||
* of EnumeratePersistedAnchorNames.
|
||||
* If PersistedAnchorCollection is not initialized block until it is initialized.
|
||||
* @param Data The exported persisted anchor data in byte array.
|
||||
* @param name the persisted anchor's name included in data.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
bool GetPersistedAnchorNameFromBuffer(const TArray<uint8>& Data, FString& name);
|
||||
|
||||
/**
|
||||
* Get the limit system can store. Use Export to save more if necessary.
|
||||
* If PersistedAnchorCollection is not initialized block until it is initialized.
|
||||
* @param maxCount the storage limit of persisted anchors.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "ViveOpenXR|SpatialAnchor|Persistence")
|
||||
void GetMaxPersistedAnchorCount(int& maxCount);
|
||||
|
||||
private:
|
||||
virtual int OnFutureComplete() override;
|
||||
|
||||
private:
|
||||
// Wait until the PAC is initialized.
|
||||
bool CheckPAC();
|
||||
|
||||
public:
|
||||
inline void* GetCollection() { return collection; }
|
||||
inline void SetReleased() { isReleased = true; collection = XR_NULL_HANDLE; }
|
||||
|
||||
private:
|
||||
void* collection;
|
||||
bool isReleased;
|
||||
};
|
||||
@@ -1,65 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.IO;
|
||||
|
||||
public class ViveOpenXRAnchor : ModuleRules
|
||||
{
|
||||
public ViveOpenXRAnchor(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
var EngineDir = Path.GetFullPath(Target.RelativeEnginePath);
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
EngineDir + "/Plugins/Runtime/OpenXR/Source/OpenXRHMD/Private",
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"ViveOpenXRHMD",
|
||||
"ViveOpenXRWrapper",
|
||||
"ViveOpenXRCommon",
|
||||
"ViveOpenXRFuture",
|
||||
"OpenXRHMD",
|
||||
"OpenXRAR",
|
||||
"AugmentedReality",
|
||||
"XRBase",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveOpenXRAsyncAction.h"
|
||||
|
||||
UViveOpenXRAsyncAction* UViveOpenXRAsyncAction::AwaitAsyncTask(UObject* WorldContextObject, UBaseTask* task)
|
||||
{
|
||||
UViveOpenXRAsyncAction* BlueprintNode = NewObject<UViveOpenXRAsyncAction>(WorldContextObject);
|
||||
BlueprintNode->WorldContextObject = WorldContextObject;
|
||||
BlueprintNode->task = task;
|
||||
// Register with the game instance to avoid being garbage collected
|
||||
BlueprintNode->RegisterWithGameInstance(WorldContextObject);
|
||||
return BlueprintNode;
|
||||
}
|
||||
|
||||
void UViveOpenXRAsyncAction::Activate()
|
||||
{
|
||||
if (task != nullptr)
|
||||
task->WaitTask();
|
||||
Complete.Broadcast();
|
||||
SetReadyToDestroy(); // garbage collected now that we're done
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) 2024 HTC Corporation. All Rights Reserved.
|
||||
#include "ViveOpenXRAsyncTask.h"
|
||||
#include "Async/Async.h"
|
||||
#include "Async/TaskGraphInterfaces.h"
|
||||
#include "OpenXRCore.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(ViveOXRAsyncTask);
|
||||
|
||||
TQueue<TWeakObjectPtr<UBaseTask>> UBaseTask::TaskQueue;
|
||||
bool UBaseTask::bIsProcessingTasks = false;
|
||||
FCriticalSection UBaseTask::QueueCriticalSection;
|
||||
|
||||
UBaseTask::UBaseTask() : isCompleted(false)
|
||||
{
|
||||
}
|
||||
|
||||
// Will called from ViveOpenXRAsyncAction::Activate
|
||||
void UBaseTask::WaitTask()
|
||||
{
|
||||
//UE_LOG(ViveOXRAsyncTask, Log, TEXT("WaitTask+"));
|
||||
// Should implement your own method to wait complete.
|
||||
while (!isCompleted) {
|
||||
//UE_LOG(ViveOXRAsyncTask, Log, TEXT("WaitTask~"));
|
||||
FPlatformProcess::Sleep(0.005f);
|
||||
}
|
||||
//UE_LOG(ViveOXRAsyncTask, Log, TEXT("WaitTask-"));
|
||||
}
|
||||
|
||||
void UBaseTask::AddTaskToQueue(TWeakObjectPtr<UBaseTask> Task)
|
||||
{
|
||||
//UE_LOG(ViveOXRAsyncTask, Log, TEXT("AddTaskToQueue+"));
|
||||
FScopeLock Lock(&QueueCriticalSection);
|
||||
TaskQueue.Enqueue(Task);
|
||||
|
||||
if (!bIsProcessingTasks)
|
||||
{
|
||||
bIsProcessingTasks = true;
|
||||
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, ProcessTasks);
|
||||
}
|
||||
//UE_LOG(ViveOXRAsyncTask, Log, TEXT("AddTaskToQueue-"));
|
||||
}
|
||||
|
||||
void UBaseTask::ProcessTasks()
|
||||
{
|
||||
//UE_LOG(ViveOXRAsyncTask, Log, TEXT("ProcessTasks+"));
|
||||
TWeakObjectPtr<UBaseTask> Task;
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
FScopeLock Lock(&QueueCriticalSection);
|
||||
if (!TaskQueue.Dequeue(Task))
|
||||
{
|
||||
//UE_LOG(ViveOXRAsyncTask, Log, TEXT("ProcessTasks Queue is empty"));
|
||||
bIsProcessingTasks = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Task.IsValid())
|
||||
{
|
||||
//UE_LOG(ViveOXRAsyncTask, Log, TEXT("ProcessTasks Task execute+"));
|
||||
Task->ExecuteTask();
|
||||
Task->isCompleted = true;
|
||||
//UE_LOG(ViveOXRAsyncTask, Log, TEXT("ProcessTasks Task execute-"));
|
||||
}
|
||||
}
|
||||
//UE_LOG(ViveOXRAsyncTask, Log, TEXT("ProcessTasks-"));
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright HTC Corporation. All Rights Reserved.
|
||||
|
||||
#include "ViveOpenXRCommon.h"
|
||||
|
||||
FViveOpenXRCommon::FViveOpenXRCommon()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
IMPLEMENT_MODULE(FViveOpenXRCommon, ViveOpenXRCommon)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user