using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Unity.Collections; using UnityEngine.Rendering; #if MODULE_URP_ENABLED using UnityEngine.Rendering.Universal; #endif // MODULE_URP_ENABLED #if MODULE_LWRP_ENABLED using UnityEngine.Rendering.LWRP; #endif // MODULE_LWRP_ENABLED using UnityEngine.Scripting; using UnityEngine.XR.ARSubsystems; namespace UnityEngine.XR.ARKit { /// /// The camera subsystem implementation for ARKit. /// [Preserve] public sealed class ARKitCameraSubsystem : XRCameraSubsystem { /// /// The identifying name for the camera-providing implementation. /// /// /// The identifying name for the camera-providing implementation. /// const string k_SubsystemId = "ARKit-Camera"; /// /// The name of the shader for rendering the camera texture. /// /// /// The name of the shader for rendering the camera texture. /// const string k_BackgroundShaderName = "Unlit/ARKitBackground"; /// /// The shader keyword for enabling LWRP rendering. /// /// /// The shader keyword for enabling LWRP rendering. /// const string k_BackgroundShaderKeywordLWRP = "ARKIT_BACKGROUND_LWRP"; /// /// The shader keyword for enabling URP rendering. /// /// /// The shader keyword for enabling URP rendering. /// const string k_BackgroundShaderKeywordURP = "ARKIT_BACKGROUND_URP"; /// /// The list of shader keywords to avoid during compilation. /// /// /// The list of shader keywords to avoid during compilation. /// static readonly List k_BackgroundShaderKeywordsToNotCompile = new List { #if !MODULE_URP_ENABLED k_BackgroundShaderKeywordURP, #endif // !MODULE_URP_ENABLED #if !MODULE_LWRP_ENABLED k_BackgroundShaderKeywordLWRP, #endif // !MODULE_LWRP_ENABLED }; /// /// Resulting values from setting the camera configuration. /// enum CameraConfigurationResult { /// /// Setting the camera configuration was successful. /// Success = 0, /// /// Setting camera configuration was not supported by the provider. /// Unsupported = 1, /// /// The given camera configuration was not valid to be set by the provider. /// InvalidCameraConfiguration = 2, /// /// The provider session was invalid. /// InvalidSession = 3, } /// /// The name for the background shader. /// /// /// The name for the background shader. /// public static string backgroundShaderName => k_BackgroundShaderName; /// /// The list of shader keywords to avoid during compilation. /// /// /// The list of shader keywords to avoid during compilation. /// internal static List backgroundShaderKeywordsToNotCompile => k_BackgroundShaderKeywordsToNotCompile; /// /// Create and register the camera subsystem descriptor to advertise a providing implementation for camera /// functionality. /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] static void Register() { if (!Api.AtLeast11_0()) return; XRCameraSubsystemCinfo cameraSubsystemCinfo = new XRCameraSubsystemCinfo { id = k_SubsystemId, providerType = typeof(ARKitCameraSubsystem.ARKitProvider), subsystemTypeOverride = typeof(ARKitCameraSubsystem), supportsAverageBrightness = false, supportsAverageColorTemperature = true, supportsColorCorrection = false, supportsDisplayMatrix = true, supportsProjectionMatrix = true, supportsTimestamp = true, supportsCameraConfigurations = true, supportsCameraImage = true, supportsAverageIntensityInLumens = true, supportsFocusModes = true, supportsFaceTrackingAmbientIntensityLightEstimation = true, supportsFaceTrackingHDRLightEstimation = true, supportsWorldTrackingAmbientIntensityLightEstimation = true, supportsWorldTrackingHDRLightEstimation = false, supportsCameraGrain = Api.AtLeast13_0(), }; if (!XRCameraSubsystem.Register(cameraSubsystemCinfo)) { Debug.LogError($"Cannot register the {k_SubsystemId} subsystem"); } } /// /// Provides the camera functionality for the ARKit implementation. /// class ARKitProvider : Provider { /// /// The shader property name for the luminance component of the camera video frame. /// /// /// The shader property name for the luminance component of the camera video frame. /// const string k_TextureYPropertyName = "_textureY"; /// /// The shader property name for the chrominance components of the camera video frame. /// /// /// The shader property name for the chrominance components of the camera video frame. /// const string k_TextureCbCrPropertyName = "_textureCbCr"; /// /// The shader property name identifier for the luminance component of the camera video frame. /// /// /// The shader property name identifier for the luminance component of the camera video frame. /// static readonly int k_TextureYPropertyNameId = Shader.PropertyToID(k_TextureYPropertyName); /// /// The shader property name identifier for the chrominance components of the camera video frame. /// /// /// The shader property name identifier for the chrominance components of the camera video frame. /// static readonly int k_TextureCbCrPropertyNameId = Shader.PropertyToID(k_TextureCbCrPropertyName); /// /// The shader keywords to enable when the Legacy RP is enabled. /// /// /// The shader keywords to enable when the Legacy RP is enabled. /// static readonly List k_LegacyRPEnabledMaterialKeywords = null; /// /// The shader keywords to disable when the Legacy RP is enabled. /// /// /// The shader keywords to disable when the Legacy RP is enabled. /// static readonly List k_LegacyRPDisabledMaterialKeywords = new List() {k_BackgroundShaderKeywordLWRP, k_BackgroundShaderKeywordURP}; /// /// Current use by Unity's rendering pipeline. /// /// /// Current use by Unity's rendering pipeline. /// static readonly RenderingThreadingMode k_RenderingThreadingMode = SystemInfo.renderingThreadingMode; /// /// Returns if the multithreaded rendering is enabled. Returns otherwise. /// /// /// Returns if the multithreaded rendering is enabled. Returns otherwise. /// static readonly bool k_MultithreadedRenderingEnabled = k_RenderingThreadingMode == RenderingThreadingMode.MultiThreaded || k_RenderingThreadingMode == RenderingThreadingMode.NativeGraphicsJobs; #if MODULE_URP_ENABLED /// /// The shader keywords to enable when URP is enabled. /// /// /// The shader keywords to enable when URP is enabled. /// static readonly List k_URPEnabledMaterialKeywords = new List() {k_BackgroundShaderKeywordURP}; /// /// The shader keywords to disable when URP is enabled. /// /// /// The shader keywords to disable when URP is enabled. /// static readonly List k_URPDisabledMaterialKeywords = new List() {k_BackgroundShaderKeywordLWRP}; #endif // MODULE_URP_ENABLED #if MODULE_LWRP_ENABLED /// /// The shader keywords to enable when LWRP is enabled. /// /// /// The shader keywords to enable when LWRP is enabled. /// static readonly List k_LWRPEnabledMaterialKeywords = new List() {k_BackgroundShaderKeywordLWRP}; /// /// The shader keywords to disable when LWRP is enabled. /// /// /// The shader keywords to disable when LWRP is enabled. /// static readonly List k_LWRPDisabledMaterialKeywords = new List() {k_BackgroundShaderKeywordURP}; #endif // MODULE_LWRP_ENABLED /// /// Get the material used by XRCameraSubsystem to render the camera texture. /// /// /// The material to render the camera texture. /// public override Material cameraMaterial => m_CameraMaterial; Material m_CameraMaterial; /// /// Whether camera permission has been granted. /// /// /// true if camera permission has been granted for this app. Otherwise, false. /// public override bool permissionGranted => NativeApi.UnityARKit_Camera_IsCameraPermissionGranted(); /// /// Constructs the ARKit camera functionality provider. /// public ARKitProvider() { NativeApi.UnityARKit_Camera_Construct(k_TextureYPropertyNameId, k_TextureCbCrPropertyNameId, k_MultithreadedRenderingEnabled); string shaderName = ARKitCameraSubsystem.backgroundShaderName; if (shaderName == null) { Debug.LogError("Cannot create camera background material compatible with the render pipeline"); } else { m_CameraMaterial = CreateCameraMaterial(shaderName); } } public override Feature currentCamera => NativeApi.UnityARKit_Camera_GetCurrentCamera(); /// /// Get the currently active camera or set the requested camera. /// public override Feature requestedCamera { get => Api.GetRequestedFeatures(); set { Api.SetFeatureRequested(Feature.AnyCamera, false); Api.SetFeatureRequested(value, true); } } /// /// Start the camera functionality. /// public override void Start() => NativeApi.UnityARKit_Camera_Start(); /// /// Stop the camera functionality. /// public override void Stop() => NativeApi.UnityARKit_Camera_Stop(); /// /// Destroy any resources required for the camera functionality. /// public override void Destroy() => NativeApi.UnityARKit_Camera_Destruct(); /// /// Get the current camera frame for the subsystem. /// /// The current Unity Camera parameters. /// The current camera frame returned by the method. /// /// true if the method successfully got a frame. Otherwise, false. /// public override bool TryGetFrame(XRCameraParams cameraParams, out XRCameraFrame cameraFrame) { return NativeApi.UnityARKit_Camera_TryGetFrame(cameraParams, out cameraFrame); } public override bool autoFocusEnabled => NativeApi.UnityARKit_Camera_GetAutoFocusEnabled(); /// /// Get or set the requested focus mode for the camera. /// public override bool autoFocusRequested { get => Api.GetRequestedFeatures().All(Feature.AutoFocus); set => Api.SetFeatureRequested(Feature.AutoFocus, value); } /// /// Gets the current light estimation mode as reported by the /// [ARSession's configuration](https://developer.apple.com/documentation/arkit/arconfiguration/2923546-lightestimationenabled). /// public override Feature currentLightEstimation => NativeApi.GetCurrentLightEstimation(); /// /// Get or set the requested light estimation mode. /// public override Feature requestedLightEstimation { get => Api.GetRequestedFeatures(); set { Api.SetFeatureRequested(Feature.AnyLightEstimation, false); Api.SetFeatureRequested(value.Intersection(Feature.AnyLightEstimation), true); } } /// /// Get the camera intrinsics information. /// /// The camera intrinsics information returned from the method. /// /// true if the method successfully gets the camera intrinsics information. Otherwise, false. /// public override bool TryGetIntrinsics(out XRCameraIntrinsics cameraIntrinsics) { return NativeApi.UnityARKit_Camera_TryGetIntrinsics(out cameraIntrinsics); } /// /// Queries the supported camera configurations. /// /// A default value used to fill the returned array before copying /// in real values. This ensures future additions to this struct are backwards compatible. /// The allocation strategy to use for the returned data. /// /// The supported camera configurations. /// public override NativeArray GetConfigurations(XRCameraConfiguration defaultCameraConfiguration, Allocator allocator) { IntPtr configurations = NativeApi.UnityARKit_Camera_AcquireConfigurations(out int configurationsCount, out int configurationSize); try { unsafe { return NativeCopyUtility.PtrToNativeArrayWithDefault(defaultCameraConfiguration, (void*)configurations, configurationSize, configurationsCount, allocator); } } finally { NativeApi.UnityARKit_Camera_ReleaseConfigurations(configurations); } } /// /// The current camera configuration. /// /// /// The current camera configuration if it exists. Otherise, null. /// /// Thrown when setting the current configuration if the given /// configuration is not a valid, supported camera configuration. /// Thrown when setting the current configuration if the /// implementation is unable to set the current camera configuration for various reasons such as: /// /// Version of iOS does not support camera configurations. /// ARKit session is invalid. /// /// public override XRCameraConfiguration? currentConfiguration { get { if (NativeApi.UnityARKit_Camera_TryGetCurrentConfiguration(out XRCameraConfiguration cameraConfiguration)) { return cameraConfiguration; } return null; } set { // Assert that the camera configuration is not null. // The XRCameraSubsystem should have already checked this. Debug.Assert(value != null, "Cannot set the current camera configuration to null"); switch (NativeApi.UnityARKit_Camera_TrySetCurrentConfiguration((XRCameraConfiguration)value)) { case CameraConfigurationResult.Success: break; case CameraConfigurationResult.Unsupported: throw new InvalidOperationException("cannot set camera configuration because ARKit version " + "does not support camera configurations"); case CameraConfigurationResult.InvalidCameraConfiguration: throw new ArgumentException("camera configuration does not exist in the available " + "configurations", "value"); case CameraConfigurationResult.InvalidSession: throw new InvalidOperationException("cannot set camera configuration because the ARKit " + "session is not valid"); default: throw new InvalidOperationException("cannot set camera configuration for ARKit"); } } } /// /// Gets the texture descriptors associated with the current camera frame. /// /// Default descriptor. /// Allocator. /// The texture descriptors. public override unsafe NativeArray GetTextureDescriptors( XRTextureDescriptor defaultDescriptor, Allocator allocator) { var textureDescriptors = NativeApi.UnityARKit_Camera_AcquireTextureDescriptors( out int length, out int elementSize); try { return NativeCopyUtility.PtrToNativeArrayWithDefault( defaultDescriptor, textureDescriptors, elementSize, length, allocator); } finally { NativeApi.UnityARKit_Camera_ReleaseTextureDescriptors(textureDescriptors); } } /// /// Get the enabled and disabled shader keywords for the material. /// /// The keywords to enable for the material. /// The keywords to disable for the material. public override void GetMaterialKeywords(out List enabledKeywords, out List disabledKeywords) { if (GraphicsSettings.currentRenderPipeline == null) { enabledKeywords = k_LegacyRPEnabledMaterialKeywords; disabledKeywords = k_LegacyRPDisabledMaterialKeywords; } #if MODULE_URP_ENABLED else if (GraphicsSettings.currentRenderPipeline is UniversalRenderPipelineAsset) { enabledKeywords = k_URPEnabledMaterialKeywords; disabledKeywords = k_URPDisabledMaterialKeywords; } #endif // MODULE_URP_ENABLED #if MODULE_LWRP_ENABLED else if (GraphicsSettings.currentRenderPipeline is LightweightRenderPipelineAsset) { enabledMaterialKeywords = k_LWRPEnabledMaterialKeywords; disabledKeywords = k_LWRPDisabledMaterialKeywords; } #endif // MODULE_LWRP_ENABLED else { enabledKeywords = null; disabledKeywords = null; } } /// /// An instance of the used to operate on objects. /// public override XRCpuImage.Api cpuImageApi => ARKitCpuImageApi.instance; /// /// Query for the latest native camera image. /// /// The metadata required to construct a /// /// true if the camera image is acquired. Otherwise, false. /// public override bool TryAcquireLatestCpuImage(out XRCpuImage.Cinfo cameraImageCinfo) => ARKitCpuImageApi.TryAcquireLatestImage(ARKitCpuImageApi.ImageType.Camera, out cameraImageCinfo); /// /// Called on the render thread by background rendering code immediately before the background /// is rendered. /// For ARKit, this is required in order to free the metal textures retained on the main thread. /// /// Platform-specific data. public override void OnBeforeBackgroundRender(int id) { // callback to schedule the release of the metal texture buffers after rendering is complete NativeApi.UnityARKit_Camera_ScheduleReleaseTextureBuffers(); } } /// /// Container to wrap the native ARKit camera APIs. /// static class NativeApi { #if UNITY_XR_ARKIT_LOADER_ENABLED [DllImport("__Internal", EntryPoint="UnityARKit_Camera_GetCurrentLightEstimation")] public static extern Feature GetCurrentLightEstimation(); [DllImport("__Internal")] public static extern void UnityARKit_Camera_Construct(int textureYPropertyNameId, int textureCbCrPropertyNameId, bool mtRenderingEnabled); [DllImport("__Internal")] public static extern void UnityARKit_Camera_Destruct(); [DllImport("__Internal")] public static extern void UnityARKit_Camera_Start(); [DllImport("__Internal")] public static extern void UnityARKit_Camera_Stop(); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_TryGetFrame(XRCameraParams cameraParams, out XRCameraFrame cameraFrame); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_TryGetIntrinsics(out XRCameraIntrinsics cameraIntrinsics); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_IsCameraPermissionGranted(); [DllImport("__Internal")] public static extern IntPtr UnityARKit_Camera_AcquireConfigurations(out int configurationsCount, out int configurationSize); [DllImport("__Internal")] public static extern void UnityARKit_Camera_ReleaseConfigurations(IntPtr configurations); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_TryGetCurrentConfiguration(out XRCameraConfiguration cameraConfiguration); [DllImport("__Internal")] public static extern CameraConfigurationResult UnityARKit_Camera_TrySetCurrentConfiguration(XRCameraConfiguration cameraConfiguration); [DllImport("__Internal")] public static extern unsafe void* UnityARKit_Camera_AcquireTextureDescriptors( out int length, out int elementSize); [DllImport("__Internal")] public static extern unsafe void UnityARKit_Camera_ReleaseTextureDescriptors( void* descriptors); [DllImport("__Internal")] public static extern Feature UnityARKit_Camera_GetCurrentCamera(); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_GetAutoFocusEnabled(); [DllImport("__Internal")] public static extern void UnityARKit_Camera_ScheduleReleaseTextureBuffers(); #else static readonly string k_ExceptionMsg = "ARKit Plugin Provider not enabled in project settings."; public static Feature GetCurrentLightEstimation() => Feature.None; public static void UnityARKit_Camera_Construct(int textureYPropertyNameId, int textureCbCrPropertyNameId, bool mtRenderingEnabled) { throw new System.NotImplementedException(k_ExceptionMsg); } public static void UnityARKit_Camera_Destruct() { throw new System.NotImplementedException(k_ExceptionMsg); } public static void UnityARKit_Camera_Start() { throw new System.NotImplementedException(k_ExceptionMsg); } public static void UnityARKit_Camera_Stop() { throw new System.NotImplementedException(k_ExceptionMsg); } public static bool UnityARKit_Camera_TryGetFrame(XRCameraParams cameraParams, out XRCameraFrame cameraFrame) { throw new System.NotImplementedException(k_ExceptionMsg); } public static bool UnityARKit_Camera_TryGetIntrinsics(out XRCameraIntrinsics cameraIntrinsics) { throw new System.NotImplementedException(k_ExceptionMsg); } public static bool UnityARKit_Camera_IsCameraPermissionGranted() => false; public static IntPtr UnityARKit_Camera_AcquireConfigurations(out int configurationsCount, out int configurationSize) { throw new System.NotImplementedException(k_ExceptionMsg); } public static void UnityARKit_Camera_ReleaseConfigurations(IntPtr configurations) { throw new System.NotImplementedException(k_ExceptionMsg); } public static bool UnityARKit_Camera_TryGetCurrentConfiguration(out XRCameraConfiguration cameraConfiguration) { throw new System.NotImplementedException(k_ExceptionMsg); } public static CameraConfigurationResult UnityARKit_Camera_TrySetCurrentConfiguration(XRCameraConfiguration cameraConfiguration) { throw new System.NotImplementedException(k_ExceptionMsg); } public static unsafe void* UnityARKit_Camera_AcquireTextureDescriptors( out int length, out int elementSize) { throw new System.NotImplementedException(k_ExceptionMsg); } public static unsafe void UnityARKit_Camera_ReleaseTextureDescriptors( void* descriptors) { throw new System.NotImplementedException(k_ExceptionMsg); } public static void UnityARKit_Camera_ScheduleReleaseTextureBuffers() { throw new System.NotImplementedException(k_ExceptionMsg); } public static Feature UnityARKit_Camera_GetCurrentCamera() => Feature.None; public static bool UnityARKit_Camera_GetAutoFocusEnabled() => false; #endif } } }