using System; using System.Collections.Generic; using Unity.Collections; using UnityEngine.XR.ARSubsystems; namespace UnityEngine.XR.ARFoundation { /// /// Manages the lifetime of the XRCameraSubsystem. Add one of these to a Camera in your scene /// if you want camera texture and light estimation information to be available. /// [DefaultExecutionOrder(ARUpdateOrder.k_CameraManager)] [DisallowMultipleComponent] [RequireComponent(typeof(Camera))] [HelpURL("https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@3.0/api/UnityEngine.XR.ARFoundation.ARCameraManager.html")] public sealed class ARCameraManager : SubsystemLifecycleManager { [SerializeField] [Tooltip("The focus mode to request on the (physical) AR camera.")] CameraFocusMode m_FocusMode = CameraFocusMode.Auto; /// /// The CameraFocusMode for the camera. /// /// /// The focus mode for the camera. /// public CameraFocusMode focusMode { get { if (subsystem != null) { return subsystem.focusMode; } else { return m_FocusMode; } } set { m_FocusMode = value; if (enabled && subsystem != null) { subsystem.focusMode = value; } } } [SerializeField] [Tooltip("The light estimation mode for the AR camera.")] LightEstimationMode m_LightEstimationMode = LightEstimationMode.Disabled; /// /// The LightEstimationMode for the camera. /// /// /// The light estimation mode for the camera. /// public LightEstimationMode lightEstimationMode { get { return m_LightEstimationMode; } set { m_LightEstimationMode = value; if (enabled && subsystem != null) subsystem.lightEstimationMode = value; } } /// /// Determines whether camera permission has been granted. /// /// /// true if permission has been granted. Otherwise, false. /// public bool permissionGranted { get { if (subsystem != null) return subsystem.permissionGranted; return false; } } /// /// An event which fires each time a new camera frame is received. /// public event Action frameReceived; /// /// The material used in background rendering. /// /// /// The material used in background rendering. /// public Material cameraMaterial { get => (subsystem == null) ? null : subsystem.cameraMaterial; } /// /// Tries to get camera intrinsics. Camera intrinsics refers to properties /// of a physical camera which may be useful when performing additional /// computer vision processing on the camera image. /// /// The camera intrinsics to be populated if the camera supports intrinsics /// /// /// true if was populated. Otherwise, false. /// public bool TryGetIntrinsics(out XRCameraIntrinsics cameraIntrinsics) { if (subsystem == null) { cameraIntrinsics = default(XRCameraIntrinsics); return false; } return subsystem.TryGetIntrinsics(out cameraIntrinsics); } /// /// Get the camera configurations currently supported for the implementation. /// /// The allocation strategy to use for the returned data. /// /// The supported camera configurations. /// public NativeArray GetConfigurations(Allocator allocator) { return ((subsystem == null) ? new NativeArray(0, allocator) : subsystem.GetConfigurations(allocator)); } /// /// The current camera configuration. /// /// /// The current camera configuration, if it exists. Otherise, null. /// /// Thrown when setting the current configuration if the /// implementation does not support camera configurations. /// Thrown when setting the current configuration if the given /// configuration is null. /// Thrown when setting the current configuration if the given /// configuration is not a supported camera configuration. /// Thrown when setting the current configuration if the /// implementation is unable to set the current camera configuration. public XRCameraConfiguration? currentConfiguration { get { return (subsystem == null) ? null : subsystem.currentConfiguration; } set { if (subsystem != null) { subsystem.currentConfiguration = value; } } } /// /// Attempt to get the latest camera image. This provides directly access to the raw pixel data, as well as /// utilities to convert to RGB and Grayscale formats. /// /// /// The XRCameraImage must be disposed to avoid resource leaks. /// /// A valid XRCameraImage if this method returns true. /// /// true if the image was acquired. Otherwise, false. /// public bool TryGetLatestImage(out XRCameraImage cameraImage) { if (subsystem == null) { cameraImage = default(XRCameraImage); return false; } return subsystem.TryGetLatestImage(out cameraImage); } void Awake() { m_Camera = GetComponent(); } /// /// Callback before the subsystem is started (but after it is created). /// protected override void OnBeforeStart() { subsystem.focusMode = m_FocusMode; subsystem.lightEstimationMode = m_LightEstimationMode; } /// /// Callback when the manager is disabled. /// protected override void OnDisable() { base.OnDisable(); foreach (var textureInfo in m_TextureInfos) { textureInfo.Dispose(); } m_TextureInfos.Clear(); } void Update() { if (subsystem == null) return; var cameraParams = new XRCameraParams { zNear = m_Camera.nearClipPlane, zFar = m_Camera.farClipPlane, screenWidth = Screen.width, screenHeight = Screen.height, screenOrientation = Screen.orientation }; XRCameraFrame frame; if (subsystem.TryGetLatestFrame(cameraParams, out frame)) { UpdateTexturesInfos(frame); if (frameReceived != null) InvokeFrameReceivedEvent(frame); } } /// /// Pull the texture descriptors from the camera subsystem, and update the texture information maintained by /// this component. /// /// The latest updated camera frame. void UpdateTexturesInfos(XRCameraFrame frame) { var textureDescriptors = subsystem.GetTextureDescriptors(Allocator.Temp); try { int numUpdated = Math.Min(m_TextureInfos.Count, textureDescriptors.Length); // Update the existing textures that are in common between the two arrays. for (int i = 0; i < numUpdated; ++i) { m_TextureInfos[i] = ARTextureInfo.GetUpdatedTextureInfo(m_TextureInfos[i], textureDescriptors[i]); } // If there are fewer textures in the current frame than we had previously, destroy any remaining unneeded // textures. if (numUpdated < m_TextureInfos.Count) { for (int i = numUpdated; i < m_TextureInfos.Count; ++i) { m_TextureInfos[i].Reset(); } m_TextureInfos.RemoveRange(numUpdated, (m_TextureInfos.Count - numUpdated)); } // Else, if there are more textures in the current frame than we have previously, add new textures for any // additional descriptors. else if (textureDescriptors.Length > m_TextureInfos.Count) { for (int i = numUpdated; i < textureDescriptors.Length; ++i) { m_TextureInfos.Add(new ARTextureInfo(textureDescriptors[i])); } } } finally { if (textureDescriptors.IsCreated) textureDescriptors.Dispose(); } } /// /// Invoke the camera frame received event packing the frame information into the event argument. /// /// The camera frame raising the event. void InvokeFrameReceivedEvent(XRCameraFrame frame) { var lightEstimation = new ARLightEstimationData(); if (frame.hasAverageBrightness) lightEstimation.averageBrightness = frame.averageBrightness; if (frame.hasAverageIntensityInLumens) lightEstimation.averageIntensityInLumens = frame.averageIntensityInLumens; if (frame.hasAverageColorTemperature) lightEstimation.averageColorTemperature = frame.averageColorTemperature; if (frame.hasColorCorrection) lightEstimation.colorCorrection = frame.colorCorrection; var eventArgs = new ARCameraFrameEventArgs(); eventArgs.lightEstimation = lightEstimation; if (frame.hasTimestamp) eventArgs.timestampNs = frame.timestampNs; if (frame.hasProjectionMatrix) eventArgs.projectionMatrix = frame.projectionMatrix; if (frame.hasDisplayMatrix) eventArgs.displayMatrix = frame.displayMatrix; if (frame.hasExposureDuration) eventArgs.exposureDuration = frame.exposureDuration; if (frame.hasExposureOffset) eventArgs.exposureOffset = frame.exposureOffset; s_Textures.Clear(); s_PropertyIds.Clear(); foreach (var textureInfo in m_TextureInfos) { s_Textures.Add(textureInfo.texture); s_PropertyIds.Add(textureInfo.descriptor.propertyNameId); } subsystem.GetMaterialKeywords(out List enabledMaterialKeywords, out ListdisabledMaterialKeywords); eventArgs.textures = s_Textures; eventArgs.propertyNameIds = s_PropertyIds; eventArgs.enabledMaterialKeywords = enabledMaterialKeywords; eventArgs.disabledMaterialKeywords = disabledMaterialKeywords; frameReceived(eventArgs); } static List s_Textures = new List(); static List s_PropertyIds = new List(); readonly List m_TextureInfos = new List(); Camera m_Camera; bool m_PreRenderInvertCullingValue; } }