Skip to content

Instantly share code, notes, and snippets.

@mikeskydev
Last active March 7, 2024 17:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikeskydev/5f52bd25cd40f67a1b753dd6abd95ffe to your computer and use it in GitHub Desktop.
Save mikeskydev/5f52bd25cd40f67a1b753dd6abd95ffe to your computer and use it in GitHub Desktop.
//Copyright Icosa Foundation
using System;
using System.Runtime.InteropServices;
using AOT;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
#if UNITY_EDITOR
using UnityEditor.XR.OpenXR.Features;
#endif
namespace IcosaFoundation.OpenXR.Unity.FBPassthrough
{
/// <summary>
/// Example feature showing how to intercept a single OpenXR function.
/// </summary>
#if UNITY_EDITOR
[OpenXRFeature(UiName = "Facebook Passthrough",
BuildTargetGroups = new []{BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
Company = "Icosa Foundation",
Desc = "Implement XR_FB_passthrough without OVR SDK",
OpenxrExtensionStrings = "XR_FB_passthrough",
Version = "0.0.1",
FeatureId = featureId)]
#endif
public class FBPassthroughFeature : OpenXRFeature
{
/// <summary>
/// The feature id string. This is used to give the feature a well known id for reference.
/// </summary>
public const string featureId = "foundation.icosa.openxr-facebook-passthrough";
/// <inheritdoc />
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
{
return func;
}
private static ulong XrInstance = 0;
private XrPassthroughFB activePassthrough = new XrPassthroughFB();
private XrPassthroughLayerFB activeLayer = new XrPassthroughLayerFB();
/// <inheritdoc />
protected override bool OnInstanceCreate(ulong xrInstance)
{
// here's one way you can grab the instance
XrInstance = xrInstance;
var success = LoadBindings();
return true;
}
/// <inheritdoc />
protected override void OnSessionCreate(ulong xrSession)
{
// here's one way you can grab the session
Debug.Log($"EXT: Got xrSession: {xrSession}");
// Test activate
var createSuccess = xrCreatePassthroughFB(
xrSession,
new XrPassthroughCreateInfoFB(XrPassthroughFlagsFB.IS_RUNNING_AT_CREATION_BIT_FB),
out activePassthrough);
var layerSuccess = xrCreatePassthroughLayerFB(xrSession, new XrPassthroughLayerCreateInfoFB(activePassthrough, XrPassthroughFlagsFB.IS_RUNNING_AT_CREATION_BIT_FB, XrPassthroughLayerPurposeFB.RECONSTRUCTION_FB), out activeLayer);
XrCompositionLayerPassthroughFB layer = new XrCompositionLayerPassthroughFB(
XrCompositionLayerFlags.BLEND_TEXTURE_SOURCE_ALPHA_BIT, activeLayer);
// TODO: submit layer somehow
}
protected override void OnSessionBegin (ulong xrSession)
{
Debug.Log($"EXT: xrBeginSession: {xrSession}");
}
protected override void OnSessionEnd(ulong xrSession)
{
Debug.Log($"EXT: about to xrEndSession: {xrSession}");
}
private delegate ulong GetInstanceProcAddrDelegate(ulong instance, string procName, out IntPtr procAddr);
private static IntPtr GetFunction(string functionName)
{
var getInstanceProcAddr = Marshal.GetDelegateForFunctionPointer<GetInstanceProcAddrDelegate>(xrGetInstanceProcAddr);
ulong result = getInstanceProcAddr(XrInstance, functionName, out var returnPtr);
if (result < 0)
{
Debug.LogWarning($"Failed to find {functionName}");
return IntPtr.Zero;
}
return returnPtr;
}
#region OpenXR native bindings and types
// https://github.com/StereoKit/StereoKit/blob/master/Examples/StereoKitTest/Tools/PassthroughFBExt.cs
enum XrStructureType : UInt64
{
XR_TYPE_PASSTHROUGH_CREATE_INFO_FB = 1000118001,
XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB = 1000118002,
XR_TYPE_PASSTHROUGH_STYLE_FB = 1000118020,
XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB = 1000118003,
}
enum XrPassthroughFlagsFB : UInt64
{
None = 0,
IS_RUNNING_AT_CREATION_BIT_FB = 0x00000001
}
enum XrCompositionLayerFlags : UInt64
{
None = 0,
CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001,
BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002,
UNPREMULTIPLIED_ALPHA_BIT = 0x00000004,
}
enum XrPassthroughLayerPurposeFB : UInt32
{
RECONSTRUCTION_FB = 0,
PROJECTED_FB = 1,
TRACKED_KEYBOARD_HANDS_FB = 1000203001,
MAX_ENUM_FB = 0x7FFFFFFF,
}
enum XrResult : UInt32
{
Success = 0,
}
#pragma warning disable 0169 // handle is not "used", but required for interop
struct XrPassthroughFB { ulong handle; }
struct XrPassthroughLayerFB { ulong handle; }
#pragma warning restore 0169
[StructLayout(LayoutKind.Sequential)]
struct XrPassthroughCreateInfoFB
{
private XrStructureType type;
public IntPtr next;
public XrPassthroughFlagsFB flags;
public XrPassthroughCreateInfoFB(XrPassthroughFlagsFB passthroughFlags)
{
type = XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_FB;
next = IntPtr.Zero;
flags = passthroughFlags;
}
}
[StructLayout(LayoutKind.Sequential)]
struct XrPassthroughLayerCreateInfoFB
{
private XrStructureType type;
public IntPtr next;
public XrPassthroughFB passthrough;
public XrPassthroughFlagsFB flags;
public XrPassthroughLayerPurposeFB purpose;
public XrPassthroughLayerCreateInfoFB(XrPassthroughFB passthrough, XrPassthroughFlagsFB flags, XrPassthroughLayerPurposeFB purpose)
{
type = XrStructureType.XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB;
next = IntPtr.Zero;
this.passthrough = passthrough;
this.flags = flags;
this.purpose = purpose;
}
}
[StructLayout(LayoutKind.Sequential)]
struct XrPassthroughStyleFB
{
public XrStructureType type;
public IntPtr next;
public float textureOpacityFactor;
public Color edgeColor;
public XrPassthroughStyleFB(float textureOpacityFactor, Color edgeColor)
{
type = XrStructureType.XR_TYPE_PASSTHROUGH_STYLE_FB;
next = IntPtr.Zero;
this.textureOpacityFactor = textureOpacityFactor;
this.edgeColor = edgeColor;
}
}
[StructLayout(LayoutKind.Sequential)]
struct XrCompositionLayerPassthroughFB
{
public XrStructureType type;
public IntPtr next;
public XrCompositionLayerFlags flags;
public ulong space;
public XrPassthroughLayerFB layerHandle;
public XrCompositionLayerPassthroughFB(XrCompositionLayerFlags flags, XrPassthroughLayerFB layerHandle)
{
type = XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB;
next = IntPtr.Zero;
space = 0;
this.flags = flags;
this.layerHandle = layerHandle;
}
}
delegate XrResult del_xrCreatePassthroughFB (ulong session, [In] XrPassthroughCreateInfoFB createInfo, out XrPassthroughFB outPassthrough);
delegate XrResult del_xrDestroyPassthroughFB (XrPassthroughFB passthrough);
delegate XrResult del_xrPassthroughStartFB (XrPassthroughFB passthrough);
delegate XrResult del_xrPassthroughPauseFB (XrPassthroughFB passthrough);
delegate XrResult del_xrCreatePassthroughLayerFB (ulong session, [In] XrPassthroughLayerCreateInfoFB createInfo, out XrPassthroughLayerFB outLayer);
delegate XrResult del_xrDestroyPassthroughLayerFB (XrPassthroughLayerFB layer);
delegate XrResult del_xrPassthroughLayerPauseFB (XrPassthroughLayerFB layer);
delegate XrResult del_xrPassthroughLayerResumeFB (XrPassthroughLayerFB layer);
delegate XrResult del_xrPassthroughLayerSetStyleFB(XrPassthroughLayerFB layer, [In] XrPassthroughStyleFB style);
del_xrCreatePassthroughFB xrCreatePassthroughFB;
del_xrDestroyPassthroughFB xrDestroyPassthroughFB;
del_xrPassthroughStartFB xrPassthroughStartFB;
del_xrPassthroughPauseFB xrPassthroughPauseFB;
del_xrCreatePassthroughLayerFB xrCreatePassthroughLayerFB;
del_xrDestroyPassthroughLayerFB xrDestroyPassthroughLayerFB;
del_xrPassthroughLayerPauseFB xrPassthroughLayerPauseFB;
del_xrPassthroughLayerResumeFB xrPassthroughLayerResumeFB;
del_xrPassthroughLayerSetStyleFB xrPassthroughLayerSetStyleFB;
bool LoadBindings()
{
xrCreatePassthroughFB = Marshal.GetDelegateForFunctionPointer<del_xrCreatePassthroughFB> (GetFunction("xrCreatePassthroughFB"));
xrDestroyPassthroughFB = Marshal.GetDelegateForFunctionPointer<del_xrDestroyPassthroughFB> (GetFunction("xrDestroyPassthroughFB"));
xrPassthroughStartFB = Marshal.GetDelegateForFunctionPointer<del_xrPassthroughStartFB> (GetFunction("xrPassthroughStartFB"));
xrPassthroughPauseFB = Marshal.GetDelegateForFunctionPointer<del_xrPassthroughPauseFB> (GetFunction("xrPassthroughPauseFB"));
xrCreatePassthroughLayerFB = Marshal.GetDelegateForFunctionPointer<del_xrCreatePassthroughLayerFB> (GetFunction("xrCreatePassthroughLayerFB"));
xrDestroyPassthroughLayerFB = Marshal.GetDelegateForFunctionPointer<del_xrDestroyPassthroughLayerFB> (GetFunction("xrDestroyPassthroughLayerFB"));
xrPassthroughLayerPauseFB = Marshal.GetDelegateForFunctionPointer<del_xrPassthroughLayerPauseFB> (GetFunction("xrPassthroughLayerPauseFB"));
xrPassthroughLayerResumeFB = Marshal.GetDelegateForFunctionPointer<del_xrPassthroughLayerResumeFB> (GetFunction("xrPassthroughLayerResumeFB"));
xrPassthroughLayerSetStyleFB = Marshal.GetDelegateForFunctionPointer<del_xrPassthroughLayerSetStyleFB>(GetFunction("xrPassthroughLayerSetStyleFB"));
return
xrCreatePassthroughFB != null &&
xrDestroyPassthroughFB != null &&
xrPassthroughStartFB != null &&
xrPassthroughPauseFB != null &&
xrCreatePassthroughLayerFB != null &&
xrDestroyPassthroughLayerFB != null &&
xrPassthroughLayerPauseFB != null &&
xrPassthroughLayerResumeFB != null &&
xrPassthroughLayerSetStyleFB != null;
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment