599 lines
22 KiB
C++
599 lines
22 KiB
C++
// Copyright 2023 PICO Inc. All Rights Reserved.
|
|
|
|
#include "PICO_Passthrough.h"
|
|
#include "PICO_PassthroughModule.h"
|
|
#include "PICOOpenXRRuntimeSettings.h"
|
|
#include "IXRTrackingSystem.h"
|
|
#include "Engine/Engine.h"
|
|
#include "OpenXRPlatformRHI.h"
|
|
#include "OpenXRCore.h"
|
|
#include "IOpenXRHMDModule.h"
|
|
|
|
#if PLATFORM_ANDROID
|
|
#include <dlfcn.h>
|
|
#endif //PLATFORM_ANDROID
|
|
|
|
FPICOOpenXRPassthrough::FPICOOpenXRPassthrough()
|
|
: Session(XR_NULL_HANDLE)
|
|
, bSupportPassthroughEXT(false)
|
|
{
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::Register()
|
|
{
|
|
RegisterOpenXRExtensionModularFeature();
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::Unregister()
|
|
{
|
|
UnregisterOpenXRExtensionModularFeature();
|
|
}
|
|
|
|
bool FPICOOpenXRPassthrough::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
|
{
|
|
OutExtensions.Add("XR_FB_passthrough");
|
|
OutExtensions.Add("XR_FB_triangle_mesh");
|
|
OutExtensions.Add("XR_FB_composition_layer_alpha_blend");
|
|
return true;
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::PostGetSystem(XrInstance InInstance, XrSystemId InSystem)
|
|
{
|
|
bSupportPassthroughEXT = IOpenXRHMDModule::Get().IsExtensionEnabled(XR_FB_PASSTHROUGH_EXTENSION_NAME) && IOpenXRHMDModule::Get().IsExtensionEnabled(XR_FB_TRIANGLE_MESH_EXTENSION_NAME);
|
|
if (bSupportPassthroughEXT)
|
|
{
|
|
XrSystemPassthroughProperties2FB passthroughSystemProperties{ XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES2_FB };
|
|
XrSystemProperties systemProperties{ XR_TYPE_SYSTEM_PROPERTIES, &passthroughSystemProperties };
|
|
xrGetSystemProperties(InInstance, InSystem, &systemProperties);
|
|
|
|
bCurrentDeviceSupportedPassthrough = (passthroughSystemProperties.capabilities & XR_PASSTHROUGH_CAPABILITY_BIT_FB) == XR_PASSTHROUGH_CAPABILITY_BIT_FB;
|
|
bPassthroughSupportColor = bCurrentDeviceSupportedPassthrough && (passthroughSystemProperties.capabilities & XR_PASSTHROUGH_CAPABILITY_COLOR_BIT_FB) == XR_PASSTHROUGH_CAPABILITY_COLOR_BIT_FB;
|
|
bPassthroughSupportDepth = bCurrentDeviceSupportedPassthrough && (passthroughSystemProperties.capabilities & XR_PASSTHROUGH_CAPABILITY_LAYER_DEPTH_BIT_FB) == XR_PASSTHROUGH_CAPABILITY_LAYER_DEPTH_BIT_FB;
|
|
UE_LOG(LogPassthroughPICO, Log, TEXT("bCurrentDeviceSupportedPassthrough:%d,bPassthroughSupportColor:%d,bPassthroughSupportDepth:%d"), bCurrentDeviceSupportedPassthrough, bPassthroughSupportColor, bPassthroughSupportDepth);
|
|
if (bCurrentDeviceSupportedPassthrough)
|
|
{
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreatePassthroughFB", (PFN_xrVoidFunction*)(&pfnXrCreatePassthroughFBX)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrDestroyPassthroughFB", (PFN_xrVoidFunction*)(&pfnXrDestroyPassthroughFBX)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrPassthroughStartFB", (PFN_xrVoidFunction*)(&pfnXrPassthroughStartFBX)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrPassthroughPauseFB", (PFN_xrVoidFunction*)(&pfnXrPassthroughPauseFBX)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreatePassthroughLayerFB", (PFN_xrVoidFunction*)(&pfnXrCreatePassthroughLayerFBX)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrDestroyPassthroughLayerFB", (PFN_xrVoidFunction*)(&pfnDestroyPassthroughLayerFBX)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrPassthroughLayerPauseFB", (PFN_xrVoidFunction*)(&pfnXrPassthroughLayerPauseFBX)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrPassthroughLayerResumeFB", (PFN_xrVoidFunction*)(&pfnXrPassthroughLayerResumeFBX)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrPassthroughLayerSetStyleFB", (PFN_xrVoidFunction*)(&pfnXrPassthroughLayerSetStyleFBX)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateGeometryInstanceFB", (PFN_xrVoidFunction*)(&pfnXrCreateGeometryInstanceF)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrDestroyGeometryInstanceFB", (PFN_xrVoidFunction*)(&pfnXrDestroyGeometryInstanceFB)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrGeometryInstanceSetTransformFB", (PFN_xrVoidFunction*)(&pfnXrGeometryInstanceSetTransformFB)));
|
|
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateTriangleMeshFB", (PFN_xrVoidFunction*)(&pfnXrCreateTriangleMeshFB)));
|
|
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrDestroyTriangleMeshFB", (PFN_xrVoidFunction*)(&pfnXrDestroyTriangleMeshFB)));
|
|
}
|
|
}
|
|
}
|
|
|
|
const void* FPICOOpenXRPassthrough::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
|
{
|
|
static FName SystemName(TEXT("OpenXR"));
|
|
if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
|
|
{
|
|
XRTrackingSystem = GEngine->XRSystem.Get();
|
|
}
|
|
|
|
return InNext;
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::PostCreateSession(XrSession InSession)
|
|
{
|
|
Session = InSession;
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace)
|
|
{
|
|
BaseSpace = TrackingSpace;
|
|
TheDisplayTime = DisplayTime;
|
|
|
|
if (IsInGameThread())
|
|
{
|
|
ENQUEUE_RENDER_COMMAND(UpdatePassthroughLayers)(
|
|
[this, PassthroughLayers = passthroughCompLayers](FRHICommandListImmediate&)
|
|
{
|
|
passthroughCompLayers_RenderThread.Empty(PassthroughLayers.Num());
|
|
for (auto Pair : PassthroughLayers)
|
|
{
|
|
passthroughCompLayers_RenderThread.Emplace(Pair.Value);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::OnBeginRendering_RenderThread(XrSession InSession)
|
|
{
|
|
UpdateInsightPassthrough();
|
|
|
|
if (InsightInitStatus == FInsightInitStatus::Initialized)
|
|
{
|
|
for (const auto& layer : passthroughCompLayers_RenderThread)
|
|
{
|
|
if (layer.Desc.HasShape<FFullScreenLayerShapePICO>())
|
|
{
|
|
const FFullScreenLayerShapePICO& FullScreenLayerProps = layer.Desc.GetShape<FFullScreenLayerShapePICO>();
|
|
UpdatePassthroughStyle_RenderThread(layer.PassthroughCompLayer.layerHandle, FullScreenLayerProps.EdgeStyleParameters);
|
|
}
|
|
else if (layer.Desc.HasShape<FCustomShapeLayerShapePICO>())
|
|
{
|
|
const FCustomShapeLayerShapePICO& CustomShapeLayerProps = layer.Desc.GetShape<FCustomShapeLayerShapePICO>();
|
|
UpdatePassthroughStyle_RenderThread(layer.PassthroughCompLayer.layerHandle, CustomShapeLayerProps.EdgeStyleParameters);
|
|
}
|
|
|
|
if (!CustomShapeGeometryMap)
|
|
{
|
|
CustomShapeGeometryMap = MakeShared<TMap<FString, FPassthroughMesh>, ESPMode::ThreadSafe>();
|
|
}
|
|
|
|
if (layer.Desc.HasShape<FCustomShapeLayerShapePICO>() && BaseSpace != XR_NULL_HANDLE)
|
|
{
|
|
const FCustomShapeLayerShapePICO& CustomShapeLayerProps = layer.Desc.GetShape<FCustomShapeLayerShapePICO>();
|
|
const TArray<FCustomShapeGeometryDescPICO>& UserGeometryList = CustomShapeLayerProps.UserGeometryList;
|
|
TSet<FString> UsedSet;
|
|
|
|
for (const FCustomShapeGeometryDescPICO& GeometryDesc : UserGeometryList)
|
|
{
|
|
const FString MeshName = GeometryDesc.MeshName;
|
|
UsedSet.Add(MeshName);
|
|
|
|
FPassthroughMesh* LayerPassthroughMesh = CustomShapeGeometryMap->Find(MeshName);
|
|
if (!LayerPassthroughMesh)
|
|
{
|
|
FPICOPassthroughMeshRef GeomPassthroughMesh = GeometryDesc.PassthroughMesh;
|
|
if (GeomPassthroughMesh)
|
|
{
|
|
XrTriangleMeshFB MeshHandle = 0;
|
|
XrGeometryInstanceFB InstanceHandle = 0;
|
|
|
|
AddPassthroughMesh_RenderThread(layer.PassthroughCompLayer.layerHandle, GeomPassthroughMesh->GetVertices(), GeomPassthroughMesh->GetTriangles(), GeometryDesc.Transform, &MeshHandle, &InstanceHandle);
|
|
CustomShapeGeometryMap->Add(MeshName, FPassthroughMesh(MeshHandle, InstanceHandle));
|
|
}
|
|
}
|
|
else if (GeometryDesc.bUpdateTransform)
|
|
{
|
|
UpdatePassthroughMeshTransform_RenderThread(LayerPassthroughMesh->InstanceHandle, GeometryDesc.Transform);
|
|
}
|
|
}
|
|
|
|
TArray<FString> ItemsToRemove;
|
|
for (auto& Entry : *CustomShapeGeometryMap)
|
|
{
|
|
if (!UsedSet.Contains(Entry.Key))
|
|
{
|
|
ItemsToRemove.Add(Entry.Key);
|
|
}
|
|
}
|
|
|
|
for (FString Entry : ItemsToRemove)
|
|
{
|
|
FPassthroughMesh* PassthroughMesh = CustomShapeGeometryMap->Find(Entry);
|
|
if (PassthroughMesh)
|
|
{
|
|
RemovePassthroughMesh_RenderThread(PassthroughMesh->MeshHandle, PassthroughMesh->InstanceHandle);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("PassthroughMesh: %s doesn't exist."), *Entry);
|
|
return;
|
|
}
|
|
|
|
CustomShapeGeometryMap->Remove(Entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::UpdatePassthroughStyle_RenderThread(XrPassthroughLayerFB LayerHandle, const FVSTEdgeStyleParametersPICO& EdgeStyleParameters)
|
|
{
|
|
XrPassthroughStyleFB Style = { XR_TYPE_PASSTHROUGH_STYLE_FB };
|
|
Style.edgeColor = XrColor4f{ 0 , 0 , 0 , 0 };
|
|
Style.textureOpacityFactor = EdgeStyleParameters.TextureOpacityFactor;
|
|
|
|
if (EdgeStyleParameters.bEnableEdgeColor)
|
|
{
|
|
Style.edgeColor = ToXrColorf(EdgeStyleParameters.EdgeColor);
|
|
}
|
|
|
|
XrPassthroughColorMapMonoToRgbaFB MonoToRgba = { XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB };
|
|
XrPassthroughColorMapMonoToMonoFB MonoToMono = { XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB };
|
|
XrPassthroughBrightnessContrastSaturationFB BrightnessContrastSaturation = { XR_TYPE_PASSTHROUGH_BRIGHTNESS_CONTRAST_SATURATION_FB };
|
|
|
|
if (EdgeStyleParameters.bEnableColorMap)
|
|
{
|
|
switch (EdgeStyleParameters.ColorMapType)
|
|
{
|
|
case PassthroughColorMapType::NoneStyle:
|
|
break;
|
|
case PassthroughColorMapType::MonoToRgba:
|
|
FMemory::Memcpy(MonoToRgba.textureColorMap, EdgeStyleParameters.ColorMapData.GetData(), EdgeStyleParameters.ColorMapData.Num() * sizeof(uint8));
|
|
Style.next = &MonoToRgba;
|
|
break;
|
|
case PassthroughColorMapType::MonoToMono:
|
|
FMemory::Memcpy(MonoToMono.textureColorMap, EdgeStyleParameters.ColorMapData.GetData(), EdgeStyleParameters.ColorMapData.Num() * sizeof(uint8));
|
|
Style.next = &MonoToMono;
|
|
break;
|
|
case PassthroughColorMapType::HandsContrast:
|
|
break;
|
|
case PassthroughColorMapType::BrightnessContrastSaturation:
|
|
BrightnessContrastSaturation.brightness = EdgeStyleParameters.Brightness * 100.0f;
|
|
BrightnessContrastSaturation.contrast = EdgeStyleParameters.Contrast;
|
|
BrightnessContrastSaturation.saturation = EdgeStyleParameters.Saturation;
|
|
|
|
Style.next = &BrightnessContrastSaturation;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
XrResult result = pfnXrPassthroughLayerSetStyleFBX(LayerHandle, &Style);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Warning, TEXT("Failed setting passthrough style"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::AddPassthroughMesh_RenderThread(XrPassthroughLayerFB Layer, const TArray<FVector>& Vertices, const TArray<int32>& Triangles, FTransform Transformation, XrTriangleMeshFB* OutMeshHandle, XrGeometryInstanceFB* OutInstanceHandle)
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
XrTriangleMeshFB MeshHandle = 0;
|
|
XrGeometryInstanceFB InstanceHandle = 0;
|
|
|
|
XrTriangleMeshCreateInfoFB MeshCreateInfo = { XR_TYPE_TRIANGLE_MESH_CREATE_INFO_FB };
|
|
MeshCreateInfo.vertexCount = Vertices.Num();
|
|
|
|
float WorldToMetersScale = XRTrackingSystem->GetWorldToMetersScale();
|
|
|
|
XrVector3f* v = new XrVector3f[Vertices.Num()];
|
|
for (int i = 0; i < Vertices.Num(); i++)
|
|
{
|
|
v[i] = ToXrVector(Vertices[i], WorldToMetersScale);
|
|
}
|
|
MeshCreateInfo.vertexBuffer = v;
|
|
|
|
MeshCreateInfo.triangleCount = Triangles.Num() / 3;
|
|
MeshCreateInfo.indexBuffer = (uint32_t*)Triangles.GetData();
|
|
MeshCreateInfo.windingOrder = XR_WINDING_ORDER_CW_FB;
|
|
|
|
if (XR_FAILED(pfnXrCreateTriangleMeshFB(Session, &MeshCreateInfo, &MeshHandle)))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Error, TEXT("Failed creating passthrough mesh surface."));
|
|
return;
|
|
}
|
|
UE_LOG(LogPassthroughPICO, Log, TEXT("Succeed creating passthrough mesh surface."));
|
|
delete []v;
|
|
|
|
FTransform TransformTrackingSpace = Transformation * XRTrackingSystem->GetTrackingToWorldTransform().Inverse();
|
|
|
|
XrGeometryInstanceCreateInfoFB CreateInfo = { XR_TYPE_GEOMETRY_INSTANCE_CREATE_INFO_FB };
|
|
CreateInfo.layer = Layer;
|
|
CreateInfo.mesh = MeshHandle;
|
|
CreateInfo.pose = ToXrPose(TransformTrackingSpace, WorldToMetersScale);
|
|
CreateInfo.scale = ToXrVector(TransformTrackingSpace.GetScale3D());
|
|
CreateInfo.baseSpace = BaseSpace;
|
|
if (XR_FAILED(pfnXrCreateGeometryInstanceF(Session, &CreateInfo, &InstanceHandle)))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Error, TEXT("Failed adding passthrough mesh surface to scene."));
|
|
return;
|
|
}
|
|
|
|
UE_LOG(LogPassthroughPICO, Log, TEXT("Succeed adding passthrough mesh surface to scene."));
|
|
|
|
*OutMeshHandle = MeshHandle;
|
|
*OutInstanceHandle = InstanceHandle;
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::UpdatePassthroughMeshTransform_RenderThread(XrGeometryInstanceFB InstanceHandle, FTransform Transformation)
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
if (InstanceHandle == XR_NULL_HANDLE || XRTrackingSystem == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float WorldToMetersScale = XRTrackingSystem->GetWorldToMetersScale();
|
|
FTransform TransformTrackingSpace = Transformation * XRTrackingSystem->GetTrackingToWorldTransform().Inverse();
|
|
|
|
XrGeometryInstanceTransformFB PoseInfo = { XR_TYPE_GEOMETRY_INSTANCE_TRANSFORM_FB };
|
|
PoseInfo.baseSpace = BaseSpace;
|
|
PoseInfo.time = TheDisplayTime;
|
|
PoseInfo.pose = ToXrPose(TransformTrackingSpace, WorldToMetersScale);
|
|
PoseInfo.scale = ToXrVector(TransformTrackingSpace.GetScale3D());
|
|
|
|
if (XR_FAILED(pfnXrGeometryInstanceSetTransformFB(InstanceHandle, &PoseInfo)))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Error, TEXT("Failed updating passthrough mesh surface transform."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::RemovePassthroughMesh_RenderThread(XrTriangleMeshFB MeshHandle, XrGeometryInstanceFB InstanceHandle)
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
if (XR_FAILED(pfnXrDestroyGeometryInstanceFB(InstanceHandle)))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Error, TEXT("Failed removing passthrough surface from scene."));
|
|
return;
|
|
}
|
|
|
|
if (XR_FAILED(pfnXrDestroyTriangleMeshFB(MeshHandle)))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Error, TEXT("Failed destroying passthrough surface mesh."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::OnDestroySession(XrSession InSession)
|
|
{
|
|
ShutdownInsightPassthrough();
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::UpdateCompositionLayers(XrSession InSession, TArray<const XrCompositionLayerBaseHeader*>& Headers)
|
|
{
|
|
if (bCurrentDeviceSupportedPassthrough && passthroughCompLayers_RenderThread.Num() > 0)
|
|
{
|
|
int InsertOrder = 0;
|
|
for (int i = 0; i < passthroughCompLayers_RenderThread.Num(); i++)
|
|
{
|
|
bool bOverlay = false;
|
|
|
|
if (passthroughCompLayers_RenderThread[i].Desc.HasShape<FFullScreenLayerShapePICO>())
|
|
{
|
|
const FFullScreenLayerShapePICO& FullScreenLayerProps = passthroughCompLayers_RenderThread[i].Desc.GetShape<FFullScreenLayerShapePICO>();
|
|
bOverlay = FullScreenLayerProps.PassthroughLayerOrder == PassthroughLayerOrder_Overlay;
|
|
}
|
|
else if (passthroughCompLayers_RenderThread[i].Desc.HasShape<FCustomShapeLayerShapePICO>())
|
|
{
|
|
const FCustomShapeLayerShapePICO& CustomShapeLayerProps = passthroughCompLayers_RenderThread[i].Desc.GetShape<FCustomShapeLayerShapePICO>();
|
|
bOverlay = CustomShapeLayerProps.PassthroughLayerOrder == PassthroughLayerOrder_Overlay;
|
|
}
|
|
|
|
if (bOverlay)
|
|
{
|
|
Headers.Add(reinterpret_cast<const XrCompositionLayerBaseHeader*>(&(passthroughCompLayers_RenderThread[i].PassthroughCompLayer)));
|
|
}
|
|
else
|
|
{
|
|
Headers.Insert(reinterpret_cast<const XrCompositionLayerBaseHeader*>(&(passthroughCompLayers_RenderThread[i].PassthroughCompLayer)), InsertOrder++);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const void* FPICOOpenXRPassthrough::OnEndProjectionLayer(XrSession InSession, int32 InLayerIndex, const void* InNext, XrCompositionLayerFlags& OutFlags)
|
|
{
|
|
if (bCurrentDeviceSupportedPassthrough)
|
|
{
|
|
BlendState = { XR_TYPE_COMPOSITION_LAYER_ALPHA_BLEND_FB };
|
|
BlendState.next = const_cast<void*>(InNext);
|
|
BlendState.srcFactorColor = XrBlendFactorFB::XR_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA_FB;
|
|
BlendState.dstFactorColor = XrBlendFactorFB::XR_BLEND_FACTOR_SRC_ALPHA_FB;
|
|
BlendState.srcFactorAlpha = XrBlendFactorFB::XR_BLEND_FACTOR_ONE_FB;
|
|
BlendState.dstFactorAlpha = XrBlendFactorFB::XR_BLEND_FACTOR_ZERO_FB;
|
|
InNext = &BlendState;
|
|
}
|
|
return InNext;
|
|
}
|
|
|
|
bool FPICOOpenXRPassthrough::GetSupportedPassthrough(bool& Support, bool& HasColor, bool& HasDepth)
|
|
{
|
|
Support = bCurrentDeviceSupportedPassthrough;
|
|
HasColor = bPassthroughSupportColor;
|
|
HasDepth = bPassthroughSupportDepth;
|
|
return bSupportPassthroughEXT;
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::UpdateInsightPassthrough()
|
|
{
|
|
const bool bShouldEnable = (InsightInitStatus == FInsightInitStatus::NotInitialized) &&
|
|
(GetMutableDefault<UPICOOpenXRRuntimeSettings>()->bEnablePassthroughEXT);
|
|
|
|
if (bShouldEnable)
|
|
{
|
|
if (InitializeInsightPassthroughOpenXR())
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Log, TEXT("Passthrough Initialized"));
|
|
InsightInitStatus = FInsightInitStatus::Initialized;
|
|
}
|
|
else
|
|
{
|
|
InsightInitStatus = FInsightInitStatus::Failed;
|
|
UE_LOG(LogPassthroughPICO, Log, TEXT("Passthrough initialization failed"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const bool bShouldShutdown = (InsightInitStatus == FInsightInitStatus::Initialized) &&
|
|
(!GetMutableDefault<UPICOOpenXRRuntimeSettings>()->bEnablePassthroughEXT);
|
|
if (bShouldShutdown)
|
|
{
|
|
ShutdownInsightPassthrough();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::ShutdownInsightPassthrough()
|
|
{
|
|
if (InsightInitStatus == FInsightInitStatus::Initialized)
|
|
{
|
|
// it may already be deinitialized.
|
|
if (!GetInsightPassthroughInitializedOpenXR() || ShutdownInsightPassthroughOpenXR())
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Log, TEXT("Passthrough shutdown"));
|
|
InsightInitStatus = FInsightInitStatus::NotInitialized;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Log, TEXT("Failed to shut down passthrough. It may be still in use."));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FPICOOpenXRPassthrough::IsInsightPassthroughSupportedOpenXR(bool* supported)
|
|
{
|
|
if (supported)
|
|
{
|
|
*supported = bCurrentDeviceSupportedPassthrough;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FPICOOpenXRPassthrough::InitializeInsightPassthroughOpenXR()
|
|
{
|
|
if (Session != XR_NULL_HANDLE && bCurrentDeviceSupportedPassthrough)
|
|
{
|
|
XrPassthroughCreateInfoFB passthroughCreateInfo = { XR_TYPE_PASSTHROUGH_CREATE_INFO_FB };
|
|
passthroughCreateInfo.flags = XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB;
|
|
|
|
XrResult result = pfnXrCreatePassthroughFBX(Session, &passthroughCreateInfo, &passthroughFeature);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Warning, TEXT("Failed to create the passthrough feature."));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FPICOOpenXRPassthrough::ShutdownInsightPassthroughOpenXR()
|
|
{
|
|
if (bCurrentDeviceSupportedPassthrough && passthroughFeature)
|
|
{
|
|
pfnXrDestroyPassthroughFBX(passthroughFeature);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FPICOOpenXRPassthrough::GetInsightPassthroughInitializedOpenXR()
|
|
{
|
|
if (bCurrentDeviceSupportedPassthrough && passthroughFeature)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32 FPICOOpenXRPassthrough::CreatePassthroughLayer(const IStereoLayers::FLayerDesc& InLayerDesc)
|
|
{
|
|
uint32 id = 0;
|
|
|
|
if (Session != XR_NULL_HANDLE && bCurrentDeviceSupportedPassthrough && passthroughFeature != XR_NULL_HANDLE)
|
|
{
|
|
XrPassthroughLayerFB passthroughLayer = XR_NULL_HANDLE;
|
|
|
|
XrPassthroughLayerCreateInfoFB layerCreateInfo = { XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB };
|
|
layerCreateInfo.passthrough = passthroughFeature;
|
|
if (InLayerDesc.HasShape<FFullScreenLayerShapePICO>())
|
|
{
|
|
layerCreateInfo.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB;
|
|
}
|
|
else if (InLayerDesc.HasShape<FCustomShapeLayerShapePICO>())
|
|
{
|
|
layerCreateInfo.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Warning, TEXT("No available passthrough layer shape!"));
|
|
return id;
|
|
}
|
|
layerCreateInfo.flags = XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB;
|
|
|
|
XrResult result = pfnXrCreatePassthroughLayerFBX(Session, &layerCreateInfo, &passthroughLayer);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Error, TEXT("Failed to create a passthrough layer"));
|
|
return id;
|
|
}
|
|
|
|
UE_LOG(LogPassthroughPICO, Log, TEXT("Succeed to create a passthrough layer"));
|
|
|
|
id = ++NextLayerIndex;//start from 1
|
|
|
|
FPassthroughLayer PassthroughLayer;
|
|
PassthroughLayer.Id = id;
|
|
PassthroughLayer.Desc = InLayerDesc;
|
|
PassthroughLayer.PassthroughFeature = passthroughFeature;
|
|
PassthroughLayer.PassthroughCompLayer = { XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB };
|
|
PassthroughLayer.PassthroughCompLayer.layerHandle = passthroughLayer;
|
|
PassthroughLayer.PassthroughCompLayer.flags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
|
|
PassthroughLayer.PassthroughCompLayer.space = XR_NULL_HANDLE;
|
|
|
|
passthroughCompLayers.Add(id, PassthroughLayer);
|
|
}
|
|
return id;
|
|
}
|
|
|
|
bool FPICOOpenXRPassthrough::DestroyPassthroughLayer(uint32 Id)
|
|
{
|
|
FPassthroughLayer* LayerFound = passthroughCompLayers.Find(Id);
|
|
if (LayerFound && (*LayerFound).PassthroughCompLayer.layerHandle != XR_NULL_HANDLE)
|
|
{
|
|
XrResult result = pfnDestroyPassthroughLayerFBX((*LayerFound).PassthroughCompLayer.layerHandle);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Warning, TEXT("Failed to destroy a passthrough layer Id:%u"), Id);
|
|
return false;
|
|
}
|
|
|
|
passthroughCompLayers.Remove(Id);
|
|
UE_LOG(LogPassthroughPICO, Log, TEXT("Succeed to destroy a passthrough layer Id:%u"), Id);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FPICOOpenXRPassthrough::PausePassthroughLayer(uint32 Id)
|
|
{
|
|
FPassthroughLayer* LayerFound = passthroughCompLayers.Find(Id);
|
|
if (LayerFound && (*LayerFound).PassthroughCompLayer.layerHandle != XR_NULL_HANDLE)
|
|
{
|
|
XrResult result = pfnXrPassthroughLayerPauseFBX((*LayerFound).PassthroughCompLayer.layerHandle);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Warning, TEXT("Failed to pause a passthrough layer Id:%u"), Id);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FPICOOpenXRPassthrough::ResumePassthroughLayer(uint32 ID)
|
|
{
|
|
FPassthroughLayer* LayerFound = passthroughCompLayers.Find(ID);
|
|
if (LayerFound && (*LayerFound).PassthroughCompLayer.layerHandle != XR_NULL_HANDLE)
|
|
{
|
|
XrResult result = pfnXrPassthroughLayerResumeFBX((*LayerFound).PassthroughCompLayer.layerHandle);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogPassthroughPICO, Warning, TEXT("Failed to resume a passthrough layer Id:%u"), ID);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FPICOOpenXRPassthrough::SetLayerDesc(uint32 LayerId, const IStereoLayers::FLayerDesc& InLayerDesc)
|
|
{
|
|
FPassthroughLayer* LayerFound = passthroughCompLayers.Find(LayerId);
|
|
|
|
if (LayerFound)
|
|
{
|
|
(*LayerFound).Desc = InLayerDesc;
|
|
}
|
|
}
|