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(typeof(ARFace))]
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 NativeArray vertices => 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 => 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 => 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 => GetUndisposable(m_FaceMesh.uvs);
///
/// 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 where the eyes are fixated 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.
internal static unsafe NativeArray GetUndisposable(NativeArray disposable) where T : struct
{
if (!disposable.IsCreated)
return default;
var undisposable = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(
disposable.GetUnsafePtr(),
disposable.Length,
Allocator.None);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(
ref undisposable,
NativeArrayUnsafeUtility.GetAtomicSafetyHandle(disposable));
#endif
return undisposable;
}
internal void UpdateMesh(XRFaceSubsystem subsystem)
{
subsystem.GetFaceMesh(sessionRelativeData.trackableId, Allocator.Persistent, ref m_FaceMesh);
m_Updated = true;
}
internal void UpdateEyes()
{
Transform CreateGameObject(string nameOfNewGameObject)
{
var newTransform = new GameObject(nameOfNewGameObject).transform;
newTransform.SetParent(transform, worldPositionStays: false);
return newTransform;
}
if (leftEye == null && rightEye == null && fixationPoint == null)
{
leftEye = CreateGameObject("Left eye");
rightEye = CreateGameObject("Right eye");
fixationPoint = CreateGameObject("Fixation point");
}
UpdateTransformFromPose(leftEye, sessionRelativeData.leftEyePose);
UpdateTransformFromPose(rightEye, sessionRelativeData.rightEyePose);
fixationPoint.localPosition = sessionRelativeData.fixationPoint;
}
static void UpdateTransformFromPose(Transform eyeTransform, Pose eyePose)
{
eyeTransform.localPosition = eyePose.position;
eyeTransform.localRotation = eyePose.rotation;
}
XRFaceMesh m_FaceMesh;
bool m_Updated;
}
}