/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * Licensed under the Oculus SDK License Agreement (the "License"); * you may not use the Oculus SDK except in compliance with the License, * which is provided at the time of installation or download, or which * otherwise accompanies this software in either electronic or hard copy form. * * You may obtain a copy of the License at * * https://developer.oculus.com/licenses/oculussdk/ * * Unless required by applicable law or agreed to in writing, the Oculus SDK * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using Unity.Collections; using UnityEngine; using static OVRPlugin; /// /// Represents an anchor. /// /// /// Scenes anchors are uniquely identified with their . /// You may dispose of an anchor by calling their method. /// public readonly struct OVRAnchor : IEquatable, IDisposable { #region Static public static readonly OVRAnchor Null = new OVRAnchor(0, Guid.Empty); internal static OVRPlugin.SpaceQueryInfo GetQueryInfo(SpaceComponentType type, OVRSpace.StorageLocation location, int maxResults, double timeout) => new OVRSpaceQuery.Options { QueryType = OVRPlugin.SpaceQueryType.Action, ActionType = OVRPlugin.SpaceQueryActionType.Load, ComponentFilter = type, Location = location, Timeout = timeout, MaxResults = maxResults, }.ToQueryInfo(); internal static OVRPlugin.SpaceQueryInfo GetQueryInfo(IEnumerable uuids, OVRSpace.StorageLocation location, double timeout) => new OVRSpaceQuery.Options { QueryType = OVRPlugin.SpaceQueryType.Action, ActionType = OVRPlugin.SpaceQueryActionType.Load, UuidFilter = uuids, Location = location, Timeout = timeout, MaxResults = OVRSpaceQuery.Options.MaxUuidCount, }.ToQueryInfo(); internal static OVRTask FetchAnchorsAsync(SpaceComponentType type, IList anchors, OVRSpace.StorageLocation location = OVRSpace.StorageLocation.Local, int maxResults = OVRSpaceQuery.Options.MaxUuidCount, double timeout = 0.0) => FetchAnchors(anchors, GetQueryInfo(type, location, maxResults, timeout)); /// /// Asynchronous method that fetches anchors with a specific component. /// /// The type of component the fetched anchor must have. /// IList that will get cleared and populated with the requested anchors.s /// Storage location to query /// The maximum number of results the query can return /// Timeout in seconds for the query. Zero indicates the query does not timeout. /// Dispose of the returned if you don't use the results /// An that will eventually let you test if the fetch was successful or not. /// If the result is true, then the parameter has been populated with the requested anchors. /// Thrown if is `null`. public static OVRTask FetchAnchorsAsync(IList anchors, OVRSpace.StorageLocation location = OVRSpace.StorageLocation.Local, int maxResults = OVRSpaceQuery.Options.MaxUuidCount, double timeout = 0.0) where T : struct, IOVRAnchorComponent { if (anchors == null) { throw new ArgumentNullException(nameof(anchors)); } return FetchAnchorsAsync(default(T).Type, anchors, location, maxResults, timeout); } /// /// Asynchronous method that fetches anchors with specifics uuids. /// /// Enumerable of uuids that anchors fetched must verify /// IList that will get cleared and populated with the requested anchors.s /// Storage location to query /// Timeout in seconds for the query. Zero indicates the query does not timeout. /// Dispose of the returned if you don't use the results /// An that will eventually let you test if the fetch was successful or not. /// If the result is true, then the parameter has been populated with the requested anchors. /// Thrown if is `null`. /// Thrown if is `null`. public static OVRTask FetchAnchorsAsync(IEnumerable uuids, IList anchors, OVRSpace.StorageLocation location = OVRSpace.StorageLocation.Local, double timeout = 0.0) { if (uuids == null) { throw new ArgumentNullException(nameof(uuids)); } if (anchors == null) { throw new ArgumentNullException(nameof(anchors)); } return FetchAnchors(anchors, GetQueryInfo(uuids, location, timeout)); } private static OVRTask FetchAnchors(IList anchors, OVRPlugin.SpaceQueryInfo queryInfo) { if (anchors == null) { throw new ArgumentNullException(nameof(anchors)); } anchors.Clear(); if (!OVRPlugin.QuerySpaces(queryInfo, out var requestId)) { return OVRTask.FromResult(false); } var task = OVRTask.FromRequest(requestId); task.SetInternalData(anchors); return task; } internal static void OnSpaceQueryCompleteData(OVRDeserialize.SpaceQueryCompleteData data) { var requestId = data.RequestId; var task = OVRTask.GetExisting(requestId); if (!task.IsPending) { return; } if (!task.TryGetInternalData>(out var anchors) || anchors == null) { task.SetResult(false); return; } if (!OVRPlugin.RetrieveSpaceQueryResults(requestId, out var rawResults, Allocator.Temp)) { task.SetResult(false); return; } foreach (var result in rawResults) { var anchor = new OVRAnchor(result.space, result.uuid); anchors.Add(anchor); } rawResults.Dispose(); task.SetResult(true); } /// /// Creates a new spatial anchor. /// /// /// Spatial anchor creation is asynchronous. This method initiates a request to create a spatial anchor at /// . The returned can be awaited or used to /// track the completion of the request. /// /// If spatial anchor creation fails, the resulting will be . /// /// The pose, in tracking space, at which you wish to create the spatial anchor. /// A task which can be used to track completion of the request. public static OVRTask CreateSpatialAnchorAsync(Pose trackingSpacePose) => CreateSpatialAnchor(new SpatialAnchorCreateInfo { BaseTracking = GetTrackingOriginType(), PoseInSpace = new Posef { Orientation = trackingSpacePose.rotation.ToFlippedZQuatf(), Position = trackingSpacePose.position.ToFlippedZVector3f(), }, Time = GetTimeInSeconds(), }, out var requestId) ? OVRTask.FromRequest(requestId) : OVRTask.FromResult(Null); /// /// Creates a new spatial anchor. /// /// /// Spatial anchor creation is asynchronous. This method initiates a request to create a spatial anchor at /// . The returned can be awaited or used to /// track the completion of the request. /// /// If spatial anchor creation fails, the resulting will be . /// /// The transform at which you wish to create the spatial anchor. /// The `Camera` associated with the Meta Quest's center eye. /// A task which can be used to track completion of the request. /// Thrown when is `null`. /// Thrown when is `null`. public static OVRTask CreateSpatialAnchorAsync(Transform transform, Camera centerEyeCamera) { if (transform == null) throw new ArgumentNullException(nameof(transform)); if (centerEyeCamera == null) throw new ArgumentNullException(nameof(centerEyeCamera)); var pose = transform.ToTrackingSpacePose(centerEyeCamera); return CreateSpatialAnchorAsync(new Pose { position = pose.position, rotation = pose.orientation, }); } #endregion internal ulong Handle { get; } /// /// Unique Identifier representing the anchor. /// public Guid Uuid { get; } internal OVRAnchor(ulong handle, Guid uuid) { Handle = handle; Uuid = uuid; } /// /// Gets the anchor's component of a specific type. /// /// The type of the component. /// The requested component. /// Make sure the anchor supports the specified type of component using /// Thrown if the anchor doesn't support the specified type of component. /// /// public T GetComponent() where T : struct, IOVRAnchorComponent { if (!TryGetComponent(out var component)) { throw new InvalidOperationException($"Anchor {Uuid} does not have component {typeof(T).Name}"); } return component; } /// /// Tries to get the anchor's component of a specific type. /// /// The requested component, as an out parameter. /// The type of the component. /// Whether or not the request succeeded. It may fail if the anchor doesn't support this type of component. /// public bool TryGetComponent(out T component) where T : struct, IOVRAnchorComponent { component = default(T); var result = OVRPlugin.GetSpaceComponentStatusInternal(Handle, component.Type, out _, out _); if (!result.IsSuccess()) { return false; } component = component.FromAnchor(this); return true; } /// /// Tests whether or not the anchor supports a specific type of component. /// /// /// For performance reasons, we use xrGetSpaceComponentStatusFB, which can /// result in an error in the logs when the component is not available. /// /// This error does not have impact on the control flow. The alternative method, /// avoids /// this error reporting, but does have performance constraints. /// /// The type of the component. /// Whether or not the specified type of component is supported. public bool SupportsComponent() where T : struct, IOVRAnchorComponent { var component = default(T); var result = OVRPlugin.GetSpaceComponentStatusInternal(Handle, component.Type, out _, out _); return result.IsSuccess(); } /// /// Get all the supported components of an anchor. /// /// /// For performance reasons, this method reuses data structures to /// avoid allocations, and is therefore not considered thread safe. /// /// Do not use in background threads, including async functions /// started with /// /// The list that will be cleared, then populated with /// the supported components. /// Whether or not the request succeeded. public bool GetSupportedComponents(List components) { components.Clear(); if (OVRPlugin.EnumerateSpaceSupportedComponents(Handle, out var componentCount, SupportedComponentsArray)) { for (var i = 0; i < componentCount; i++) components.Add(SupportedComponentsArray[i]); return true; } return false; } private static readonly SpaceComponentType[] SupportedComponentsArray = new SpaceComponentType[64]; public bool Equals(OVRAnchor other) => Handle.Equals(other.Handle) && Uuid.Equals(other.Uuid); public override bool Equals(object obj) => obj is OVRAnchor other && Equals(other); public static bool operator ==(OVRAnchor lhs, OVRAnchor rhs) => lhs.Equals(rhs); public static bool operator !=(OVRAnchor lhs, OVRAnchor rhs) => !lhs.Equals(rhs); public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + Uuid.GetHashCode()); public override string ToString() => Uuid.ToString(); /// /// Disposes of an anchor. /// /// /// Calling this method will destroy the anchor so that it won't be managed by internal systems until /// the next time it is fetched again. /// public void Dispose() => OVRPlugin.DestroySpace(Handle); }