/*
* 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);
}