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);
}
}
}