using System; using System.Collections.Generic; using Unity.Collections; using UnityEngine.Scripting; using UnityEngine.XR.ARSubsystems; namespace UnityEngine.XR.MagicLeap { #if UNITY_XR_MAGICLEAP_PROVIDER using MLLog = UnityEngine.XR.MagicLeap.MagicLeapLogger; #endif /// /// The Magic Leap implementation of the XRAnchorSubsystem. Do not create this directly. /// Use XRAnchorSubsystemDescriptor.Create() instead. /// [Preserve] public sealed partial class AnchorSubsystem : XRAnchorSubsystem { #if !UNITY_2020_2_OR_NEWER protected override Provider CreateProvider() => new MagicLeapProvider(); #endif class MagicLeapProvider : Provider { /// /// The squared amount by which a coordinate frame has to move for its anchor to be reported as "updated" /// const float CoordinateFramePositionEpsilonSquared = .0001f; const ulong AnchorTrackableIdSalt = 0xf52b75076e45ad88; static TrackableId GenerateTrackableId(string id) { var hash = id.GetHashCode(); var trackableId = new TrackableId((ulong)hash, AnchorTrackableIdSalt); return trackableId; } static bool PosesAreApproximatelyEqual(Pose lhs, Pose rhs) { var positionDelta = lhs.position - rhs.position; var rotationDelta = lhs.rotation * Quaternion.Inverse(rhs.rotation); return positionDelta.sqrMagnitude <= CoordinateFramePositionEpsilonSquared; } private Camera mainCamera; private MLAnchors.LocalizationInfo localizationInfo; private MLAnchors.Request query = new MLAnchors.Request(); private Dictionary currentAnchors = new Dictionary(); public MagicLeapProvider() { } public override void Start() => mainCamera = Camera.main; public override void Stop() => mainCamera = null; public override void Destroy() { } public override unsafe TrackableChanges GetChanges(XRAnchor defaultAnchor, Allocator allocator) { if (mainCamera == null) return default; MLAnchors.GetLocalizationInfo(out localizationInfo); if (localizationInfo.LocalizationStatus != MLAnchors.LocalizationStatus.Localized) return default; query.Start(new MLAnchors.Request.Params(mainCamera.transform.position, 0, 0, false)); var mlResult = query.TryGetResult(out MLAnchors.Request.Result result); if (!mlResult.IsOk) return default; var added = new NativeFixedList((int)result.anchors.Length, Allocator.Temp); var updated = new NativeFixedList((int)result.anchors.Length, Allocator.Temp); var removed = new NativeFixedList((int)currentAnchors.Count, Allocator.Temp); for (int i = 0; i < result.anchors.Length; ++i) { var anchor = result.anchors[i]; var trackableId = GenerateTrackableId(anchor.Id); // added if (!currentAnchors.ContainsKey(trackableId)) { var xrAnchor = new XRAnchor(trackableId, anchor.Pose, TrackingState.Tracking, IntPtr.Zero); added.Add(xrAnchor); } // updated else { var currentAnchor = currentAnchors[trackableId]; currentAnchors.Remove(trackableId); if (!PosesAreApproximatelyEqual(currentAnchor.Pose, anchor.Pose)) { var xrAnchor = new XRAnchor(trackableId, anchor.Pose, TrackingState.Tracking, IntPtr.Zero); updated.Add(xrAnchor); } } } // removed foreach (var remainingAnchor in currentAnchors.Values) { var trackableId = GenerateTrackableId(remainingAnchor.Id); removed.Add(trackableId); } // reinitialize currentAnchors in place to avoid garbage collection. // cannot just add the updated anchors back in because some exiting anchors may not have qualified into the updated list. currentAnchors.Clear(); foreach (var newAnchor in result.anchors) { var trackableId = GenerateTrackableId(newAnchor.Id); currentAnchors.Add(trackableId, newAnchor); } using (added) using (updated) using (removed) { var changes = new TrackableChanges( added.Length, updated.Length, removed.Length, allocator); added.CopyTo(changes.added); updated.CopyTo(changes.updated); removed.CopyTo(changes.removed); Debug.Log("num added: " + added.Length + ", num updated: " + updated.Length + ", num removed: " + removed.Length); return changes; } } public override unsafe bool TryAddAnchor(Pose pose, out XRAnchor xrAnchor) { if (localizationInfo.LocalizationStatus != MLAnchors.LocalizationStatus.Localized) { xrAnchor = default; return false; } var result = MLAnchors.Anchor.Create(pose, 100, out MLAnchors.Anchor anchor); if (!result.IsOk) { xrAnchor = default; return false; } result = anchor.Publish(); if (!result.IsOk) { xrAnchor = default; return false; } xrAnchor = new XRAnchor(GenerateTrackableId(anchor.Id), anchor.Pose, TrackingState.Tracking, IntPtr.Zero); return result.IsOk; } public override bool TryRemoveAnchor(TrackableId trackableId) { if (!currentAnchors.ContainsKey(trackableId)) return false; var anchor = currentAnchors[trackableId]; var result = anchor.Delete(); return result.IsOk; } } #if UNITY_XR_MAGICLEAP_PROVIDER [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] #endif static void RegisterDescriptor() { XRAnchorSubsystemDescriptor.Create(new XRAnchorSubsystemDescriptor.Cinfo { id = MagicLeapXrProvider.AnchorSubsystemId, #if UNITY_2020_2_OR_NEWER providerType = typeof(AnchorSubsystem.MagicLeapProvider), subsystemTypeOverride = typeof(AnchorSubsystem), #else subsystemImplementationType = typeof(AnchorSubsystem), #endif supportsTrackableAttachments = false }); } } }