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 for the shader for rendering the camera texture. /// /// /// The name for 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 UNITY_IOS && !UNITY_EDITOR XRCameraSubsystemCinfo cameraSubsystemCinfo = new XRCameraSubsystemCinfo { id = k_SubsystemId, implementationType = typeof(ARKitCameraSubsystem), supportsAverageBrightness = false, supportsAverageColorTemperature = true, supportsColorCorrection = false, supportsDisplayMatrix = true, supportsProjectionMatrix = true, supportsTimestamp = true, supportsCameraConfigurations = true, supportsCameraImage = true, supportsAverageIntensityInLumens = true, supportsFocusModes = true, }; if (!XRCameraSubsystem.Register(cameraSubsystemCinfo)) { Debug.LogErrorFormat("Cannot register the {0} subsystem", k_SubsystemId); } #endif // UNITY_IOS && !UNITY_EDITOR } /// /// Create the ARKit camera functionality provider for the camera subsystem. /// /// /// The ARKit camera functionality provider for the camera subsystem. /// protected override Provider CreateProvider() => new ARKitProvider(); /// /// 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}; #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); string shaderName = ARKitCameraSubsystem.backgroundShaderName; if (shaderName == null) { Debug.LogError("Cannot create camera background material compatible with the render pipeline"); } else { m_CameraMaterial = CreateCameraMaterial(shaderName); } } /// /// 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); } /// /// Set the focus mode for the camera. /// /// The focus mode to set for the camera. /// /// true if the method successfully set the focus mode for the camera. Otherwise, false. /// public override CameraFocusMode cameraFocusMode { get => NativeApi.UnityARKit_Camera_GetFocusMode(); set => NativeApi.UnityARKit_Camera_SetFocusMode(value); } /// /// Set the light estimation mode. /// /// The light estimation mode to set. /// /// true if the method successfully set the light estimation mode. Otherwise, false. /// public override bool TrySetLightEstimationMode(LightEstimationMode lightEstimationMode) { return NativeApi.UnityARKit_Camera_TrySetLightEstimationMode(lightEstimationMode); } /// /// Get the camera intrinisics 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) { int configurationsCount; int configurationSize; IntPtr configurations = NativeApi.UnityARKit_Camera_AcquireConfigurations(out configurationsCount, out 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 { XRCameraConfiguration cameraConfiguration; if (NativeApi.UnityARKit_Camera_TryGetCurrentConfiguration(out 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 th current camera /// frame. /// /// Default descriptor. /// Allocator. /// The texture descriptors. public unsafe override NativeArray GetTextureDescriptors( XRTextureDescriptor defaultDescriptor, Allocator allocator) { int length, elementSize; var textureDescriptors = NativeApi.UnityARKit_Camera_AcquireTextureDescriptors( out length, out 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.renderPipelineAsset == null) { enabledKeywords = k_LegacyRPEnabledMaterialKeywords; disabledKeywords = k_LegacyRPDisabledMaterialKeywords; } #if MODULE_URP_ENABLED else if (GraphicsSettings.renderPipelineAsset is UniversalRenderPipelineAsset) { enabledKeywords = k_URPEnabledMaterialKeywords; disabledKeywords = k_URPDisabledMaterialKeywords; } #endif // MODULE_URP_ENABLED #if MODULE_LWRP_ENABLED else if (GraphicsSettings.renderPipelineAsset is LightweightRenderPipelineAsset) { enabledMaterialKeywords = k_LWRPEnabledMaterialKeywords; disabledKeywords = k_LWRPDisabledMaterialKeywords; } #endif // MODULE_LWRP_ENABLED else { enabledKeywords = null; disabledKeywords = null; } } /// /// 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 TryAcquireLatestImage(out CameraImageCinfo cameraImageCinfo) { return NativeApi.UnityARKit_Camera_TryAcquireLatestImage(out cameraImageCinfo); } /// /// Get the status of an existing asynchronous conversion request. /// /// The unique identifier associated with a request. /// The state of the request. /// public override AsyncCameraImageConversionStatus GetAsyncRequestStatus(int requestId) { return NativeApi.UnityARKit_Camera_GetAsyncRequestStatus(requestId); } /// /// Dispose an existing native image identified by . /// /// A unique identifier for this camera image. /// public override void DisposeImage(int nativeHandle) => NativeApi.UnityARKit_Camera_DisposeImage(nativeHandle); /// /// Dispose an existing async conversion request. /// /// A unique identifier for the request. /// public override void DisposeAsyncRequest(int requestId) => NativeApi.UnityARKit_Camera_DisposeAsyncRequest(requestId); /// /// Get information about an image plane from a native image handle by index. /// /// A unique identifier for this camera image. /// The index of the plane to get. /// The returned camera plane information if successful. /// /// true if the image plane was acquired. Otherwise, false. /// /// public override bool TryGetPlane( int nativeHandle, int planeIndex, out CameraImagePlaneCinfo planeCinfo) { return NativeApi.UnityARKit_Camera_TryGetPlane(nativeHandle, planeIndex, out planeCinfo); } /// /// Determine whether a native image handle returned by is currently /// valid. An image may become invalid if it has been disposed. /// /// /// If a handle is valid, and should not fail. /// /// A unique identifier for the camera image in question. /// true, if it is a valid handle. Otherwise, false. /// public override bool NativeHandleValid(int nativeHandle) => NativeApi.UnityARKit_Camera_HandleValid(nativeHandle); /// /// Get the number of bytes required to store an image with the iven dimensions and TextureFormat. /// /// A unique identifier for the camera image to convert. /// The dimensions of the output image. /// The TextureFormat for the image. /// The number of bytes required to store the converted image. /// true if the output was set. public override bool TryGetConvertedDataSize( int nativeHandle, Vector2Int dimensions, TextureFormat format, out int size) { return NativeApi.UnityARKit_Camera_TryGetConvertedDataSize(nativeHandle, dimensions, format, out size); } /// /// Convert the image with handle using the provided /// . /// /// A unique identifier for the camera image to convert. /// The parameters to use during the conversion. /// A buffer to write the converted image to. /// The number of bytes available in the buffer. /// /// true if the image was converted and stored in . /// public override bool TryConvert( int nativeHandle, XRCameraImageConversionParams conversionParams, IntPtr destinationBuffer, int bufferLength) { return NativeApi.UnityARKit_Camera_TryConvert( nativeHandle, conversionParams, destinationBuffer, bufferLength); } /// /// Create an asynchronous request to convert a camera image, similar to except /// the conversion should happen on a thread other than the calling (main) thread. /// /// A unique identifier for the camera image to convert. /// The parameters to use during the conversion. /// A unique identifier for this request. public override int ConvertAsync( int nativeHandle, XRCameraImageConversionParams conversionParams) { return NativeApi.UnityARKit_Camera_CreateAsyncConversionRequest(nativeHandle, conversionParams); } /// /// Get a pointer to the image data from a completed asynchronous request. This method should only succeed /// if returns . /// /// The unique identifier associated with a request. /// A pointer to the native buffer containing the data. /// The number of bytes in . /// true if and were set and point /// to the image data. public override bool TryGetAsyncRequestData(int requestId, out IntPtr dataPtr, out int dataLength) { return NativeApi.UnityARKit_Camera_TryGetAsyncRequestData(requestId, out dataPtr, out dataLength); } /// /// Similar to but takes a delegate to /// invoke when the request is complete, rather than returning a request id. /// /// /// If the first parameter to is /// then the dataPtr parameter must be valid /// for the duration of the invocation. The data may be destroyed immediately upon return. The /// parameter must be passed back to the . /// /// A unique identifier for the camera image to convert. /// The parameters to use during the conversion. /// A delegate which must be invoked when the request is complete, whether the /// conversion was successfully or not. /// A native pointer which must be passed back unaltered to /// . public override void ConvertAsync( int nativeHandle, XRCameraImageConversionParams conversionParams, OnImageRequestCompleteDelegate callback, IntPtr context) { NativeApi.UnityARKit_Camera_CreateAsyncConversionRequestWithCallback( nativeHandle, conversionParams, callback, context); } } /// /// Container to wrap the native ARKit camera APIs. /// static class NativeApi { [DllImport("__Internal")] public static extern void UnityARKit_Camera_Construct(int textureYPropertyNameId, int textureCbCrPropertyNameId); [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 void UnityARKit_Camera_SetFocusMode(CameraFocusMode cameraFocusMode); [DllImport("__Internal")] public static extern CameraFocusMode UnityARKit_Camera_GetFocusMode(); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_TrySetLightEstimationMode(LightEstimationMode lightEstimationMode); [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 unsafe extern void* UnityARKit_Camera_AcquireTextureDescriptors( out int length, out int elementSize); [DllImport("__Internal")] public static unsafe extern void UnityARKit_Camera_ReleaseTextureDescriptors( void* descriptors); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_TryAcquireLatestImage(out CameraImageCinfo cameraImageCinfo); [DllImport("__Internal")] public static extern AsyncCameraImageConversionStatus UnityARKit_Camera_GetAsyncRequestStatus(int requestId); [DllImport("__Internal")] public static extern void UnityARKit_Camera_DisposeImage( int nativeHandle); [DllImport("__Internal")] public static extern void UnityARKit_Camera_DisposeAsyncRequest( int requestHandle); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_TryGetPlane(int nativeHandle, int planeIndex, out CameraImagePlaneCinfo planeCinfo); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_HandleValid( int nativeHandle); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_TryGetConvertedDataSize( int nativeHandle, Vector2Int dimensions, TextureFormat format, out int size); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_TryConvert( int nativeHandle, XRCameraImageConversionParams conversionParams, IntPtr buffer, int bufferLength); [DllImport("__Internal")] public static extern int UnityARKit_Camera_CreateAsyncConversionRequest( int nativeHandle, XRCameraImageConversionParams conversionParams); [DllImport("__Internal")] public static extern bool UnityARKit_Camera_TryGetAsyncRequestData( int requestHandle, out IntPtr dataPtr, out int dataLength); [DllImport("__Internal")] public static extern void UnityARKit_Camera_CreateAsyncConversionRequestWithCallback( int nativeHandle, XRCameraImageConversionParams conversionParams, XRCameraSubsystem.OnImageRequestCompleteDelegate callback, IntPtr context); } } }