/******************************************************************************* Copyright © 2015-2022 PICO Technology Co., Ltd.All rights reserved. NOTICE:All information contained herein is, and remains the property of PICO Technology Co., Ltd. The intellectual and technical concepts contained herein are proprietary to PICO Technology Co., Ltd. and may be covered by patents, patents in process, and are protected by trade secret or copyright law. Dissemination of this information or reproduction of this material is strictly forbidden unless prior written permission is obtained from PICO Technology Co., Ltd. *******************************************************************************/ using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.XR.Management; using UnityEngine.XR; using AOT; #if UNITY_INPUT_SYSTEM using UnityEngine.InputSystem; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.XR; using Unity.XR.PXR.Input; using System.Linq; #endif #if UNITY_EDITOR using UnityEditor; #endif #if AR_FOUNDATION using UnityEngine.XR.ARSubsystems; #endif #if XR_HANDS using UnityEngine.XR.Hands; #endif namespace Unity.XR.PXR { #if UNITY_INPUT_SYSTEM #if UNITY_EDITOR [InitializeOnLoad] #endif static class InputLayoutLoader { static InputLayoutLoader() { RegisterInputLayouts(); } public static void RegisterInputLayouts() { InputSystem.RegisterLayout(matches: new InputDeviceMatcher().WithInterface(XRUtilities.InterfaceMatchAnyVersion).WithProduct(@"^(PICO HMD)|^(PICO Neo)|^(PICO G)")); InputSystem.RegisterLayout(matches: new InputDeviceMatcher().WithInterface(XRUtilities.InterfaceMatchAnyVersion).WithProduct(@"^(PICO Controller)")); } } #endif public class PXR_Loader : XRLoaderHelper #if UNITY_EDITOR , IXRLoaderPreInit #endif { private const string TAG = "PXR_Loader"; private static List displaySubsystemDescriptors = new List(); private static List inputSubsystemDescriptors = new List(); private static List meshSubsystemDescriptors = new List(); #if XR_HANDS private static List handSubsystemDescriptors = new List(); #endif #if AR_FOUNDATION private static List sessionSubsystemDescriptors = new List(); private static List cameraSubsystemDescriptors = new List(); private static List faceSubsystemDescriptors = new List(); private static List humanBodySubsystemDescriptors = new List(); private static List anchorSubsystemDescriptors = new List(); #endif public delegate Quaternion ConvertRotationWith2VectorDelegate(Vector3 from, Vector3 to); public XRDisplaySubsystem displaySubsystem { get { return GetLoadedSubsystem(); } } public XRInputSubsystem inputSubsystem { get { return GetLoadedSubsystem(); } } public XRMeshSubsystem meshSubsystem { get { return GetLoadedSubsystem(); } } internal enum LoaderState { Uninitialized, InitializeAttempted, Initialized, StartAttempted, Started, StopAttempted, Stopped, DeinitializeAttempted } internal LoaderState currentLoaderState { get; private set; } = LoaderState.Uninitialized; List validLoaderInitStates = new List { LoaderState.Uninitialized, LoaderState.InitializeAttempted }; List validLoaderStartStates = new List { LoaderState.Initialized, LoaderState.StartAttempted, LoaderState.Stopped }; List validLoaderStopStates = new List { LoaderState.StartAttempted, LoaderState.Started, LoaderState.StopAttempted }; List validLoaderDeinitStates = new List { LoaderState.InitializeAttempted, LoaderState.Initialized, LoaderState.Stopped, LoaderState.DeinitializeAttempted }; List runningStates = new List() { LoaderState.Initialized, LoaderState.StartAttempted, LoaderState.Started }; public override bool Initialize() { Debug.Log($"{TAG} Initialize() currentLoaderState={currentLoaderState}"); #if UNITY_INPUT_SYSTEM InputLayoutLoader.RegisterInputLayouts(); #endif #if UNITY_ANDROID && !UNITY_EDITOR PXR_Settings settings = GetSettings(); if (settings != null) { UserDefinedSettings userDefinedSettings = new UserDefinedSettings { stereoRenderingMode = settings.GetStereoRenderingMode(), colorSpace = (ushort)((QualitySettings.activeColorSpace == ColorSpace.Linear) ? 1 : 0), useContentProtect = Convert.ToUInt16(PXR_ProjectSetting.GetProjectConfig().useContentProtect), systemDisplayFrequency = settings.GetSystemDisplayFrequency(), optimizeBufferDiscards = settings.GetOptimizeBufferDiscards(), enableAppSpaceWarp = Convert.ToUInt16(settings.enableAppSpaceWarp), enableSubsampled = Convert.ToUInt16(PXR_ProjectSetting.GetProjectConfig().enableSubsampled), lateLatchingDebug = Convert.ToUInt16(PXR_ProjectSetting.GetProjectConfig().latelatchingDebug), enableStageMode = Convert.ToUInt16(PXR_ProjectSetting.GetProjectConfig().stageMode), enableSuperResolution = Convert.ToUInt16(PXR_ProjectSetting.GetProjectConfig().superResolution), normalSharpening = Convert.ToUInt16(PXR_ProjectSetting.GetProjectConfig().normalSharpening), qualitySharpening = Convert.ToUInt16(PXR_ProjectSetting.GetProjectConfig().qualitySharpening), fixedFoveatedSharpening = Convert.ToUInt16(PXR_ProjectSetting.GetProjectConfig().fixedFoveatedSharpening), selfAdaptiveSharpening = Convert.ToUInt16(PXR_ProjectSetting.GetProjectConfig().selfAdaptiveSharpening), spatialMeshLod = Convert.ToUInt16(PXR_ProjectSetting.GetProjectConfig().meshLod), }; PXR_Plugin.System.UPxr_Construct(ConvertRotationWith2Vector); PXR_Plugin.System.UPxr_SetEventDataBufferCallBack(EventDataBufferFunction); PXR_Plugin.System.UPxr_SetUserDefinedSettings(userDefinedSettings); } #endif PXR_Plugin.System.ProductName = PXR_Plugin.System.UPxr_GetProductName(); if (currentLoaderState == LoaderState.Initialized) return true; if (!validLoaderInitStates.Contains(currentLoaderState)) return false; if (displaySubsystem == null) { CreateSubsystem(displaySubsystemDescriptors, "PICO Display"); if (displaySubsystem == null) return false; } if (inputSubsystem == null) { CreateSubsystem(inputSubsystemDescriptors, "PICO Input"); if (inputSubsystem == null) return false; } if (PXR_ProjectSetting.GetProjectConfig().spatialMesh) { CreateSubsystem(meshSubsystemDescriptors, "PICO Mesh"); } #if XR_HANDS CreateSubsystem(handSubsystemDescriptors, "PICO Hands"); #endif #if AR_FOUNDATION if (PXR_ProjectSetting.GetProjectConfig().arFoundation) { CreateSubsystem(sessionSubsystemDescriptors, PXR_SessionSubsystem.k_SubsystemId); CreateSubsystem(cameraSubsystemDescriptors, PXR_CameraSubsystem.k_SubsystemId); if (PXR_ProjectSetting.GetProjectConfig().faceTracking) { CreateSubsystem(faceSubsystemDescriptors, PXR_FaceSubsystem.k_SubsystemId); } if (PXR_ProjectSetting.GetProjectConfig().bodyTracking) { CreateSubsystem(humanBodySubsystemDescriptors, PXR_HumanBodySubsystem.k_SubsystemId); } if (PXR_ProjectSetting.GetProjectConfig().spatialAnchor) { CreateSubsystem(anchorSubsystemDescriptors, PXR_AnchorSubsystem.k_SubsystemId); } } #endif if (displaySubsystem == null && inputSubsystem == null) { Debug.LogError("PXRLog Unable to start PICO Plugin."); } else if (displaySubsystem == null) { Debug.LogError("PXRLog Failed to load display subsystem."); } else if (inputSubsystem == null) { Debug.LogError("PXRLog Failed to load input subsystem."); } else { PXR_Plugin.System.UPxr_InitializeFocusCallback(); } #if XR_HANDS var handSubSystem = GetLoadedSubsystem(); if (handSubSystem == null) { Debug.LogError("PXRLog Failed to load XRHandSubsystem."); } #endif if (PXR_ProjectSetting.GetProjectConfig().spatialAnchor) { PXR_Plugin.MixedReality.UPxr_CreateSpatialAnchorSenseDataProvider(); } if (PXR_ProjectSetting.GetProjectConfig().sceneCapture) { PXR_Plugin.MixedReality.UPxr_CreateSceneCaptureSenseDataProvider(); } currentLoaderState = LoaderState.Initialized; return displaySubsystem != null; } public override bool Start() { Debug.Log($"{TAG} Start() currentLoaderState={currentLoaderState}"); if (currentLoaderState == LoaderState.Started) return true; if (!validLoaderStartStates.Contains(currentLoaderState)) return false; currentLoaderState = LoaderState.StartAttempted; StartSubsystem(); StartSubsystem(); #if XR_HANDS StartSubsystem(); #endif #if AR_FOUNDATION if (PXR_ProjectSetting.GetProjectConfig().arFoundation) { StartSubsystem(); if (PXR_ProjectSetting.GetProjectConfig().bodyTracking) { StartSubsystem(); } if (PXR_ProjectSetting.GetProjectConfig().faceTracking) { StartSubsystem(); } } if (PXR_ProjectSetting.GetProjectConfig().spatialAnchor) { StartSubsystem(); } #endif if (!displaySubsystem?.running ?? false) { StartSubsystem(); } if (!inputSubsystem?.running ?? false) { StartSubsystem(); } currentLoaderState = LoaderState.Started; return true; } public override bool Stop() { Debug.Log($"{TAG} Stop() currentLoaderState={currentLoaderState}"); if (currentLoaderState == LoaderState.Stopped) return true; if (!validLoaderStopStates.Contains(currentLoaderState)) return false; currentLoaderState = LoaderState.StopAttempted; var inputRunning = inputSubsystem?.running ?? false; var displayRunning = displaySubsystem?.running ?? false; if (inputRunning) { StopSubsystem(); } if (displayRunning) { StopSubsystem(); } #if XR_HANDS StopSubsystem(); #endif #if AR_FOUNDATION if (PXR_ProjectSetting.GetProjectConfig().arFoundation) { StopSubsystem(); if (PXR_ProjectSetting.GetProjectConfig().bodyTracking) { StopSubsystem(); } if (PXR_ProjectSetting.GetProjectConfig().faceTracking) { StopSubsystem(); } } if (PXR_ProjectSetting.GetProjectConfig().spatialAnchor) { StopSubsystem(); } #endif currentLoaderState = LoaderState.Stopped; return true; } public override bool Deinitialize() { Debug.Log($"{TAG} Deinitialize() currentLoaderState={currentLoaderState}"); if (currentLoaderState == LoaderState.Uninitialized) return true; if (!validLoaderDeinitStates.Contains(currentLoaderState)) { return false; } currentLoaderState = LoaderState.DeinitializeAttempted; DestroySubsystem(); DestroySubsystem(); if (PXR_ProjectSetting.GetProjectConfig().spatialMesh) { if (meshSubsystem.running) { StopSubsystem(); } PXR_Plugin.MixedReality.UPxr_DisposeMesh(); DestroySubsystem(); } #if XR_HANDS DestroySubsystem(); #endif #if AR_FOUNDATION if (PXR_ProjectSetting.GetProjectConfig().arFoundation) { DestroySubsystem(); if (PXR_ProjectSetting.GetProjectConfig().bodyTracking) { DestroySubsystem(); } if (PXR_ProjectSetting.GetProjectConfig().faceTracking) { DestroySubsystem(); } } #endif PXR_Plugin.System.UPxr_DeinitializeFocusCallback(); if (PXR_ProjectSetting.GetProjectConfig().spatialAnchor) { PXR_MixedReality.GetSenseDataProviderState(PxrSenseDataProviderType.SpatialAnchor, out var providerState); if (providerState == PxrSenseDataProviderState.Running) { PXR_MixedReality.StopSenseDataProvider(PxrSenseDataProviderType.SpatialAnchor); } PXR_Plugin.MixedReality.UPxr_DestroySenseDataProvider(PXR_Plugin.MixedReality.UPxr_GetSenseDataProviderHandle(PxrSenseDataProviderType.SpatialAnchor)); } if (PXR_ProjectSetting.GetProjectConfig().sceneCapture) { PXR_MixedReality.GetSenseDataProviderState(PxrSenseDataProviderType.SceneCapture, out var providerState); if (providerState == PxrSenseDataProviderState.Running) { PXR_MixedReality.StopSenseDataProvider(PxrSenseDataProviderType.SceneCapture); } PXR_Plugin.MixedReality.UPxr_DestroySenseDataProvider(PXR_Plugin.MixedReality.UPxr_GetSenseDataProviderHandle(PxrSenseDataProviderType.SceneCapture)); } currentLoaderState = LoaderState.Uninitialized; return true; } [MonoPInvokeCallback(typeof(ConvertRotationWith2VectorDelegate))] static Quaternion ConvertRotationWith2Vector(Vector3 from, Vector3 to) { return Quaternion.FromToRotation(from, to); } [MonoPInvokeCallback(typeof(EventDataBufferCallBack))] static void EventDataBufferFunction(ref PxrEventDataBuffer eventDB) { int status, action; switch (eventDB.type) { case PxrStructureType.SessionStateChanged: int state = BitConverter.ToInt32(eventDB.data, 0); if (PXR_Plugin.System.SessionStateChanged != null) { PXR_Plugin.System.SessionStateChanged(state); } #if AR_FOUNDATION PXR_SessionSubsystem.instance?.OnSessionStateChange(state); #endif break; case PxrStructureType.Controller: PxrDeviceEventType eventType = (PxrDeviceEventType)eventDB.data[0]; status = eventDB.data[5]; action = eventDB.data[6]; PLog.d(TAG, $"Controller eventType={eventType}, status={status}, action={action}"); switch (eventType) { case PxrDeviceEventType.INPUTDEVICE_CHANGED: if (PXR_Plugin.System.InputDeviceChanged != null) { PXR_Plugin.System.InputDeviceChanged(status); } break; case PxrDeviceEventType.MOTION_TRACKER_STATE: if (status == 0 || status == 1) { if (PXR_MotionTracking.MotionTrackerNumberOfConnections != null) { PXR_MotionTracking.MotionTrackerNumberOfConnections(status, action); } } else if (status == 2) { if (PXR_MotionTracking.BodyTrackingAbnormalCalibrationData != null) { PXR_MotionTracking.BodyTrackingAbnormalCalibrationData(status, action); } } break; case PxrDeviceEventType.MOTION_TRACKER_BATTERY: if (PXR_MotionTracking.MotionTrackerBatteryLevel != null) { PXR_MotionTracking.MotionTrackerBatteryLevel(status, action); } break; case PxrDeviceEventType.BODYTRACKING_STATE_ERROR_CODE: if (PXR_MotionTracking.BodyTrackingStateError != null) { PXR_MotionTracking.BodyTrackingStateError((BodyTrackingStatusCode)status, (BodyTrackingErrorCode)action); } break; case PxrDeviceEventType.BODYTRACKING_ACTION: if (PXR_MotionTracking.BodyTrackingAction != null) { if ((action & (int)BodyActionList.PxrTouchGround) != 0) { PXR_MotionTracking.BodyTrackingAction(status, BodyActionList.PxrTouchGround); } if ((action & (int)BodyActionList.PxrKeepStatic) != 0) { PXR_MotionTracking.BodyTrackingAction(status, BodyActionList.PxrKeepStatic); } if ((action & (int)BodyActionList.PxrTouchGroundToe) != 0) { PXR_MotionTracking.BodyTrackingAction(status, BodyActionList.PxrTouchGroundToe); } if ((action & (int)BodyActionList.PxrFootDownAction) != 0) { PXR_MotionTracking.BodyTrackingAction(status, BodyActionList.PxrFootDownAction); } } break; } break; case PxrStructureType.SeethroughStateChanged: status = BitConverter.ToInt32(eventDB.data, 0); PXR_Plugin.Boundary.seeThroughState = status; if (PXR_Plugin.Boundary.SeethroughStateChangedAction != null) { PXR_Plugin.Boundary.SeethroughStateChangedAction(status); } PLog.i(TAG, $"SeethroughStateChanged status ={status}"); break; case PxrStructureType.RefreshRateChanged: float drRate = BitConverter.ToSingle(eventDB.data, 0); if (PXR_Plugin.System.DisplayRefreshRateChangedAction != null) { PXR_Plugin.System.DisplayRefreshRateChangedAction(drRate); } PLog.i(TAG, $"RefreshRateChanged value ={drRate}"); break; case PxrStructureType.SDKLoglevelChanged: status = BitConverter.ToInt32(eventDB.data, 0); PLog.logLevel = (PLog.LogLevel)status; PLog.i(TAG, $"SDKLoglevelChanged logLevel ={status}"); break; case PxrStructureType.MotionTrackerKeyEvent: if (PXR_MotionTracking.MotionTrackerKeyAction != null) { MotionTrackerEventData result = new MotionTrackerEventData(); result.trackerSN.value = System.Text.Encoding.ASCII.GetString(eventDB.data.Take(24).ToArray()); result.code = BitConverter.ToInt32(eventDB.data, 24); result.action = BitConverter.ToInt32(eventDB.data, 28); result.repeat = BitConverter.ToInt32(eventDB.data, 32); result.shortPress = BitConverter.ToBoolean(eventDB.data, 36); PLog.i(TAG, $" code={result.code}, action={result.action}, repeat={result.repeat}, shortPress={result.shortPress},trackerSN={result.trackerSN.value} "); PXR_MotionTracking.MotionTrackerKeyAction(result); } break; case PxrStructureType.EXTDevConnectStateEvent: if (PXR_MotionTracking.ExtDevConnectAction != null) { ExtDevConnectEventData result = new ExtDevConnectEventData(); result.trackerSN.value = System.Text.Encoding.ASCII.GetString(eventDB.data.Take(24).ToArray()); result.state = BitConverter.ToInt32(eventDB.data, 24); PLog.i(TAG, $" state={result.state},trackerSN={result.trackerSN.value} "); PXR_MotionTracking.ExtDevConnectAction(result); } break; case PxrStructureType.EXTDevBatteryStateEvent: if (PXR_MotionTracking.ExtDevBatteryAction != null) { ExtDevBatteryEventData result = new ExtDevBatteryEventData(); result.trackerSN.value = System.Text.Encoding.ASCII.GetString(eventDB.data.Take(24).ToArray()); result.battery = BitConverter.ToInt32(eventDB.data, 24); result.charger = BitConverter.ToInt32(eventDB.data, 28); PLog.i(TAG, $" state={result.battery}, charger={result.charger}, trackerSN={result.trackerSN.value} "); PXR_MotionTracking.ExtDevBatteryAction(result); } break; case PxrStructureType.MotionTrackingModeChangedEvent: if (PXR_MotionTracking.MotionTrackingModeChangedAction != null) { status = BitConverter.ToInt32(eventDB.data, 0); PLog.i(TAG, $" status={status} "); PXR_MotionTracking.MotionTrackingModeChangedAction((MotionTrackerMode)status); } break; case PxrStructureType.EXTDevPassDataEvent: if (PXR_MotionTracking.ExtDevPassDataAction != null) { status = BitConverter.ToInt32(eventDB.data, 0); PLog.i(TAG, $" state={status}"); PXR_MotionTracking.ExtDevPassDataAction(status); } break; default: break; } } public PXR_Settings GetSettings() { PXR_Settings settings = null; #if UNITY_EDITOR UnityEditor.EditorBuildSettings.TryGetConfigObject("Unity.XR.PXR.Settings", out settings); #endif #if UNITY_ANDROID && !UNITY_EDITOR settings = PXR_Settings.settings; #endif return settings; } #if UNITY_EDITOR public string GetPreInitLibraryName(BuildTarget buildTarget, BuildTargetGroup buildTargetGroup) { return "PxrPlatform"; } #endif #if UNITY_ANDROID && !UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)] static void RuntimeLoadPicoPlugin() { PXR_Plugin.System.UPxr_LoadPICOPlugin(); string version = "UnityXR_" + PXR_Plugin.System.UPxr_GetSDKVersion() + "_" + Application.unityVersion; PXR_Plugin.System.UPxr_SetConfigString( ConfigType.EngineVersion, version ); } #endif } }