// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Runtime.InteropServices;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.OpenXR.Features;
#endif
using VIVE.OpenXR.SecondaryViewConfiguration;
namespace VIVE.OpenXR.FirstPersonObserver
{
///
/// Name: FirstPersonObserver.cs
/// Role: OpenXR FirstPersonObserver Extension Class
/// Responsibility: The OpenXR extension implementation and its lifecycles logic in OpenXR
///
#if UNITY_EDITOR
[OpenXRFeature(UiName = "XR MSFT First Person Observer",
BuildTargetGroups = new[] { BuildTargetGroup.Android },
Company = "HTC",
Desc = "Request the application to render an additional first-person view of the scene.",
DocumentationLink = "..\\Documentation",
OpenxrExtensionStrings = OPEN_XR_EXTENSION_STRING,
Version = "1.0.0",
FeatureId = FeatureId,
Hidden = true)]
#endif
public class ViveFirstPersonObserver : OpenXRFeature
{
private static ViveFirstPersonObserver _instance;
///
/// ViveFirstPersonObserver static instance (Singleton).
///
public static ViveFirstPersonObserver Instance
{
get
{
if (_instance == null)
{
_instance =
OpenXRSettings.Instance.GetFeature();
}
return _instance;
}
}
///
/// The log identification.
///
private const string LogTag = "VIVE.OpenXR.FirstPersonObserver";
///
/// The feature id string. This is used to give the feature a well known id for reference.
///
public const string FeatureId = "vive.openxr.feature.firstpersonobserver";
///
/// OpenXR specification 12.114. XR_MSFT_first_person_observer.
///
public const string OPEN_XR_EXTENSION_STRING = "XR_MSFT_first_person_observer";
///
/// The flag represents whether the OpenXR loader created an instance or not.
///
private bool XrInstanceCreated { get; set; } = false;
///
/// The instance created through xrCreateInstance.
///
private XrInstance XrInstance { get; set; } = 0;
///
/// The function delegate declaration of xrGetInstanceProcAddr.
///
private OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr { get; set; }
#region OpenXR life-cycle events
///
/// Called after xrCreateInstance.
///
/// Handle of the xrInstance.
/// Returns true if successful. Returns false otherwise.
protected override bool OnInstanceCreate(ulong xrInstance)
{
if (!IsExtensionEnabled())
{
Warning($"OnInstanceCreate() {OPEN_XR_EXTENSION_STRING} or " +
$"{ViveSecondaryViewConfiguration.OPEN_XR_EXTENSION_STRING} is NOT enabled.");
return false;
}
XrInstanceCreated = true;
XrInstance = xrInstance;
Debug("OnInstanceCreate() " + XrInstance);
if (!GetXrFunctionDelegates(XrInstance))
{
Error("Get function pointer of OpenXRFunctionPointerAccessor failed.");
return false;
}
Debug("Get function pointer of OpenXRFunctionPointerAccessor succeed.");
return base.OnInstanceCreate(xrInstance);
}
#endregion
///
/// Get the OpenXR function via XrInstance.
///
/// The XrInstance is provided by the Unity OpenXR Plugin.
/// Return true if get successfully. False otherwise.
private bool GetXrFunctionDelegates(XrInstance xrInstance)
{
if (xrGetInstanceProcAddr != IntPtr.Zero)
{
Debug("Get function pointer of openXRFunctionPointerAccessor.");
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(xrGetInstanceProcAddr,
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
if (XrGetInstanceProcAddr == null)
{
Error(
"Get function pointer of openXRFunctionPointerAccessor failed due to the XrGetInstanceProcAddr is null.");
return false;
}
}
else
{
Error(
"Get function pointer of openXRFunctionPointerAccessor failed due to the xrGetInstanceProcAddr is null.");
return false;
}
return true;
}
#region Utilities functions
///
/// Check ViveFirstPersonObserver extension is enabled or not.
///
/// Return true if enabled. False otherwise.
public static bool IsExtensionEnabled()
{
return OpenXRRuntime.IsExtensionEnabled(OPEN_XR_EXTENSION_STRING) &&
ViveSecondaryViewConfiguration.IsExtensionEnabled();
}
///
/// Print log with tag "VIVE.OpenXR.SecondaryViewConfiguration".
///
/// The log you want to print.
private static void Debug(string msg)
{
UnityEngine.Debug.Log(LogTag + " " + msg);
}
///
/// Print warning message with tag "VIVE.OpenXR.SecondaryViewConfiguration".
///
/// The warning message you want to print.
private static void Warning(string msg)
{
UnityEngine.Debug.LogWarning(LogTag + " " + msg);
}
///
/// Print an error message with the tag "VIVE.OpenXR.SecondaryViewConfiguration."
///
/// The error message you want to print.
private static void Error(string msg)
{
UnityEngine.Debug.LogError(LogTag + " " + msg);
}
#endregion
}
}