using System.Runtime.InteropServices; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using UnityEngine.Scripting; using UnityEngine.XR.ARSubsystems; namespace UnityEngine.XR.ARKit { /// /// This subsystem provides implementing functionality for the XRHumanBodySubsystem class. /// [Preserve] public sealed class ARKitHumanBodySubsystem : XRHumanBodySubsystem { /// /// Register the ARKit human body subsystem if iOS and not the editor. /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] static void Register() { if (!Api.AtLeast13_0()) return; const string k_SubsystemId = "ARKit-HumanBody"; var humanBodySubsystemCinfo = new XRHumanBodySubsystemDescriptor.Cinfo() { id = k_SubsystemId, providerType = typeof(ARKitHumanBodySubsystem.ARKitProvider), subsystemTypeOverride = typeof(ARKitHumanBodySubsystem), supportsHumanBody2D = NativeApi.UnityARKit_HumanBodyProvider_DoesSupportBodyPose2DEstimation(), supportsHumanBody3D = NativeApi.UnityARKit_HumanBodyProvider_DoesSupportBodyPose3DEstimation(), supportsHumanBody3DScaleEstimation = NativeApi.UnityARKit_HumanBodyProvider_DoesSupportBodyPose3DScaleEstimation(), }; XRHumanBodySubsystemDescriptor.Register(humanBodySubsystemCinfo); } /// /// The implementation provider class. /// class ARKitProvider : Provider { /// /// Construct the implementation provider. /// public ARKitProvider() => NativeApi.UnityARKit_HumanBodyProvider_Construct(); /// /// Start the provider. /// public override void Start() => NativeApi.UnityARKit_HumanBodyProvider_Start(); /// /// Stop the provider. /// public override void Stop() => NativeApi.UnityARKit_HumanBodyProvider_Stop(); /// /// Destroy the human body subsystem by first ensuring that the subsystem has been stopped and then /// destroying the provider. /// public override void Destroy() => NativeApi.UnityARKit_HumanBodyProvider_Destruct(); /// /// Sets whether human body pose 2D estimation is enabled. /// /// Whether the human body pose 2D estimation should be enabled. /// /// /// true if the human body pose 2D estimation is set to the given value. Otherwise, false. /// /// /// Current restrictions limit either human body pose estimation to be enabled or human segmentation images /// to be enabled. At this time, these features are mutually exclusive. /// public override bool pose2DRequested { get => Api.GetRequestedFeatures().All(Feature.Body2D); set => Api.SetFeatureRequested(Feature.Body2D, value); } /// /// Tests whether 2D body tracking is enabled. /// public override bool pose2DEnabled => NativeApi.UnityARKit_HumanBodyProvider_GetHumanBodyPose2DEstimationEnabled(); /// /// Sets whether human body pose 3D estimation is enabled. /// /// Whether the human body pose 3D estimation should be enabled. /// /// /// true if the human body pose 3D estimation is set to the given value. Otherwise, false. /// /// /// Current restrictions limit either human body pose estimation to be enabled or human segmentation images /// to be enabled. At this time, these features are mutually exclusive. /// public override bool pose3DRequested { get => Api.GetRequestedFeatures().All(Feature.Body3D); set => Api.SetFeatureRequested(Feature.Body3D, value); } /// /// Tests whether 3D body tracking is enabled. /// public override bool pose3DEnabled => NativeApi.UnityARKit_HumanBodyProvider_GetHumanBodyPose3DEstimationEnabled(); public override bool pose3DScaleEstimationRequested { get => Api.GetRequestedFeatures().All(Feature.Body3DScaleEstimation); set => Api.SetFeatureRequested(Feature.Body3DScaleEstimation, value); } public override bool pose3DScaleEstimationEnabled => NativeApi.UnityARKit_HumanBodyProvider_GetHumanBodyPose3DScaleEstimationEnabled(); /// /// Queries for the set of human body changes. /// /// The default human body. /// The memory allocator to use for the returns trackable changes. /// /// The set of human body changes. /// public override unsafe TrackableChanges GetChanges(XRHumanBody defaultHumanBody, Allocator allocator) { var context = NativeApi.UnityARKit_HumanBodyProvider_AcquireChanges( out var numAddedHumanBodies, out var addedHumanBodiesPointer, out var numUpdatedHumanBodies, out var updatedHumanBodiesPointer, out var numRemovedHumanBodyIds, out var removedHumanBodyIdsPointer, out var stride); try { // Wrap the navite pointers into a native array and then copy them into a separate native array enabled // with temporary allocations. return new TrackableChanges( addedHumanBodiesPointer, numAddedHumanBodies, updatedHumanBodiesPointer, numUpdatedHumanBodies, removedHumanBodyIdsPointer, numRemovedHumanBodyIds, defaultHumanBody, stride, allocator); } finally { NativeApi.UnityARKit_HumanBodyProvider_ReleaseChanges(context); } } /// /// Get the skeleton joints for the requested trackable identifier. /// /// The human body trackable identifier for which to query. /// The memory allocator to use for the returned arrays. /// The array of skeleton joints to update and return. public override unsafe void GetSkeleton(TrackableId trackableId, Allocator allocator, ref NativeArray skeleton) { int numJoints; void* joints = NativeApi.UnityARKit_HumanBodyProvider_AcquireJoints(trackableId, out numJoints); try { if (joints == null) { numJoints = 0; } if (!skeleton.IsCreated || (skeleton.Length != numJoints)) { if (skeleton.IsCreated) { skeleton.Dispose(); } skeleton = new NativeArray(numJoints, allocator); } if (joints != null) { var tmp = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray( joints, numJoints, Allocator.None); skeleton.CopyFrom(tmp); } } finally { NativeApi.UnityARKit_HumanBodyProvider_ReleaseJoints(joints); } } /// /// Gets the human body pose 2D joints for the current frame. /// /// The default value for the body pose 2D joint. /// The width of the screen, in pixels. /// The height of the screen, in pixels. /// The orientation of the device so that the joint positions can be /// adjusted as required. /// The allocator to use for the returned array memory. /// /// The array of body pose 2D joints. /// /// /// The returned array can be empty if the system does not detect a human in the camera image. /// public override unsafe NativeArray GetHumanBodyPose2DJoints( XRHumanBodyPose2DJoint defaultHumanBodyPose2DJoint, int screenWidth, int screenHeight, ScreenOrientation screenOrientation, Allocator allocator) { var joints = NativeApi.UnityARKit_HumanBodyProvider_AcquireHumanBodyPose2DJoints( screenWidth, screenHeight, screenOrientation, out var length, out var elementSize); try { var returnJoints = NativeCopyUtility.PtrToNativeArrayWithDefault( defaultHumanBodyPose2DJoint, joints, elementSize, length, allocator); return returnJoints; } finally { NativeApi.UnityARKit_HumanBodyProvider_ReleaseHumanBodyPose2DJoints(joints); } } } /// /// Container to wrap the native ARKit human body APIs. /// static class NativeApi { #if UNITY_XR_ARKIT_LOADER_ENABLED [DllImport("__Internal")] public static extern void UnityARKit_HumanBodyProvider_Construct(); [DllImport("__Internal")] public static extern void UnityARKit_HumanBodyProvider_Start(); [DllImport("__Internal")] public static extern void UnityARKit_HumanBodyProvider_Stop(); [DllImport("__Internal")] public static extern void UnityARKit_HumanBodyProvider_Destruct(); [DllImport("__Internal")] public static extern unsafe void* UnityARKit_HumanBodyProvider_AcquireChanges( out int numAddedHumanBodies, out void* addedBodys, out int numUpdatedHumanBodies, out void* updatedBodys, out int numRemovedHumanBodyIds, out void* removedBodyIds, out int stride); [DllImport("__Internal")] public static extern unsafe void UnityARKit_HumanBodyProvider_ReleaseChanges(void* context); [DllImport("__Internal")] public static extern unsafe void* UnityARKit_HumanBodyProvider_AcquireJoints(TrackableId trackableId, out int numJoints); [DllImport("__Internal")] public static extern unsafe void UnityARKit_HumanBodyProvider_ReleaseJoints(void* joints); [DllImport("__Internal")] public static unsafe extern void* UnityARKit_HumanBodyProvider_AcquireHumanBodyPose2DJoints( int screenWidth, int screenHeight, ScreenOrientation screenOrientation, out int length, out int elementSize); [DllImport("__Internal")] public static unsafe extern void UnityARKit_HumanBodyProvider_ReleaseHumanBodyPose2DJoints(void* joints); [DllImport("__Internal")] public static extern bool UnityARKit_HumanBodyProvider_GetHumanBodyPose2DEstimationEnabled(); [DllImport("__Internal")] public static extern bool UnityARKit_HumanBodyProvider_GetHumanBodyPose3DEstimationEnabled(); [DllImport("__Internal")] public static extern bool UnityARKit_HumanBodyProvider_GetHumanBodyPose3DScaleEstimationEnabled(); [DllImport("__Internal")] public static extern bool UnityARKit_HumanBodyProvider_DoesSupportBodyPose2DEstimation(); [DllImport("__Internal")] public static extern bool UnityARKit_HumanBodyProvider_DoesSupportBodyPose3DEstimation(); [DllImport("__Internal")] public static extern bool UnityARKit_HumanBodyProvider_DoesSupportBodyPose3DScaleEstimation(); #else static readonly string k_ExceptionMsg = "Apple ARKit XR Plug-in Provider not enabled in project settings."; public static void UnityARKit_HumanBodyProvider_Construct() { throw new System.NotImplementedException(k_ExceptionMsg); } public static void UnityARKit_HumanBodyProvider_Start() { throw new System.NotImplementedException(k_ExceptionMsg); } public static void UnityARKit_HumanBodyProvider_Stop() { throw new System.NotImplementedException(k_ExceptionMsg); } public static void UnityARKit_HumanBodyProvider_Destruct() { throw new System.NotImplementedException(k_ExceptionMsg); } public static unsafe void* UnityARKit_HumanBodyProvider_AcquireChanges( out int numAddedHumanBodies, out void* addedBodys, out int numUpdatedHumanBodies, out void* updatedBodys, out int numRemovedHumanBodyIds, out void* removedBodyIds, out int stride) { throw new System.NotImplementedException(k_ExceptionMsg); } public static unsafe void UnityARKit_HumanBodyProvider_ReleaseChanges(void* context) { throw new System.NotImplementedException(k_ExceptionMsg); } public static unsafe void* UnityARKit_HumanBodyProvider_AcquireJoints(TrackableId trackableId, out int numJoints) { throw new System.NotImplementedException(k_ExceptionMsg); } public static unsafe void UnityARKit_HumanBodyProvider_ReleaseJoints(void* joints) { throw new System.NotImplementedException(k_ExceptionMsg); } public static unsafe void* UnityARKit_HumanBodyProvider_AcquireHumanBodyPose2DJoints( int screenWidth, int screenHeight, ScreenOrientation screenOrientation, out int length, out int elementSize) { throw new System.NotImplementedException(k_ExceptionMsg); } public static unsafe void UnityARKit_HumanBodyProvider_ReleaseHumanBodyPose2DJoints(void* joints) { throw new System.NotImplementedException(k_ExceptionMsg); } public static bool UnityARKit_HumanBodyProvider_GetHumanBodyPose2DEstimationEnabled() { throw new System.NotImplementedException(k_ExceptionMsg); } public static bool UnityARKit_HumanBodyProvider_GetHumanBodyPose3DEstimationEnabled() { throw new System.NotImplementedException(k_ExceptionMsg); } public static bool UnityARKit_HumanBodyProvider_GetHumanBodyPose3DScaleEstimationEnabled() { throw new System.NotImplementedException(k_ExceptionMsg); } public static bool UnityARKit_HumanBodyProvider_DoesSupportBodyPose2DEstimation() { throw new System.NotImplementedException(k_ExceptionMsg); } public static bool UnityARKit_HumanBodyProvider_DoesSupportBodyPose3DEstimation() { throw new System.NotImplementedException(k_ExceptionMsg); } public static bool UnityARKit_HumanBodyProvider_DoesSupportBodyPose3DScaleEstimation() { throw new System.NotImplementedException(k_ExceptionMsg); } #endif } } }