using System; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using UnityEngine.XR.ARSubsystems; namespace UnityEngine.XR.ARFoundation { /// /// Represents a face detected by an AR device. /// /// /// Generated by the when an AR device detects /// a face in the environment. /// [DisallowMultipleComponent] [DefaultExecutionOrder(ARUpdateOrder.k_Face)] [HelpURL("https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@3.0/api/UnityEngine.XR.ARFoundation.ARFace.html")] public sealed class ARFace : ARTrackable { /// /// Invoked when the face is updated. If face meshes are supported, there will be /// updated , , , and /// . /// public event Action updated; /// /// The vertices representing the face mesh. Check for existence with vertices.IsCreated. /// This array is parallel to and . Vertices are /// provided in face space, that is, relative to this 's local /// position and rotation. /// public unsafe NativeArray vertices { get { return GetUndisposable(m_FaceMesh.vertices); } } /// /// The normals representing the face mesh. Check for existence with normals.IsCreated. /// This array is parallel to and . /// public unsafe NativeArray normals { get { return GetUndisposable(m_FaceMesh.normals); } } /// /// The indices defining the triangles of the face mesh. Check for existence with indices.IsCreated. /// The are three times as many indices as triangles, so this will always be a multiple of 3. /// public NativeArray indices { get { return GetUndisposable(m_FaceMesh.indices); } } /// /// The texture coordinates representing the face mesh. Check for existence with uvs.IsCreated. /// This array is parallel to and . /// public NativeArray uvs { get { return GetUndisposable(m_FaceMesh.uvs); } } /// /// Get a native pointer associated with this face. /// /// /// The data pointed to by this member is implementation defined. /// The lifetime of the pointed to object is also /// implementation defined, but should be valid at least until the next /// update. /// public IntPtr nativePtr => sessionRelativeData.nativePtr; /// /// The [transform](https://docs.unity3d.com/ScriptReference/Transform.html) of the left eye of the face, or `null` if there is no data for the left eye. /// public Transform leftEye { get; private set; } /// /// The [transform](https://docs.unity3d.com/ScriptReference/Transform.html) of the right eye of the face, or `null` if there is no data for the right eye. /// public Transform rightEye { get; private set; } /// /// The [transform](https://docs.unity3d.com/ScriptReference/Transform.html) representing the point at which the eyes are fixated upon or `null` if there is no fixation data. /// public Transform fixationPoint { get; private set; } void Update() { if (m_Updated && updated != null) { updated(new ARFaceUpdatedEventArgs(this)); m_Updated = false; } } void OnDestroy() { m_FaceMesh.Dispose(); } // Creates an alias to the same array, but the caller cannot Dispose it. unsafe NativeArray GetUndisposable(NativeArray disposable) where T : struct { if (!disposable.IsCreated) return default(NativeArray); return NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray( disposable.GetUnsafePtr(), disposable.Length, Allocator.None); } internal void UpdateMesh(XRFaceSubsystem subsystem) { subsystem.GetFaceMesh(sessionRelativeData.trackableId, Allocator.Persistent, ref m_FaceMesh); m_Updated = true; } internal void UpdateEyes() { if (leftEye == null && rightEye == null && fixationPoint == null) { leftEye = Instantiate(new GameObject(), transform).transform; rightEye = Instantiate(new GameObject(), transform).transform; fixationPoint = Instantiate(new GameObject(), transform).transform; } UpdateTransformFromPose(leftEye, sessionRelativeData.leftEyePose); UpdateTransformFromPose(rightEye, sessionRelativeData.rightEyePose); fixationPoint.localPosition = sessionRelativeData.fixationPoint; } private void UpdateTransformFromPose(Transform eyeTransform, Pose eyePose) { eyeTransform.localPosition = eyePose.position; eyeTransform.localRotation = eyePose.rotation; } XRFaceMesh m_FaceMesh; bool m_Updated; } }