// 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 } }