// %BANNER_BEGIN%
// ---------------------------------------------------------------------
// %COPYRIGHT_BEGIN%
// Copyright (c) (2021-2022) Magic Leap, Inc. All Rights Reserved.
// Use of this file is governed by the Software License Agreement, located here: https://www.magicleap.com/software-license-agreement-ml2
// Terms and conditions applicable to third-party materials accompanying this distribution may also be found in the top-level NOTICE file appearing herein.
// %COPYRIGHT_END%
// ---------------------------------------------------------------------
// %BANNER_END%
using System;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
namespace UnityEngine.XR.MagicLeap
{
public partial class PlanesSubsystem
{
///
/// Container for the boundary of a detected planar surface. This is specific
/// to Magic Leap because the polygon describing the boundary may be concave,
/// and may contain holes.
///
public struct PlaneBoundary : IEquatable
{
///
/// Whether this is valid. You should check
/// for validity before invoking ,
/// , , or
/// .
///
public bool valid
{
get
{
unsafe
{
return m_Boundary.polygon != null;
}
}
}
///
/// Gets the polygon representing a plane's boundary, and, if successful, copies it to .
/// is resized or created using if necessary.
/// The 2D vertices are in plane-space.
///
/// The index of the boundary to retrieve.
/// The Allocator to use if must be recreated.
/// Must be Allocator.TempJob or Allocator.Persistent.
/// A NativeArray to fill with boundary points. If the array is not the correct size, it is disposed and recreated.
/// Thrown if is false.
/// Thrown if is Allocator.Temp or Allocator.None.
public unsafe void GetPolygon(Allocator allocator, ref NativeArray polygonOut)
{
if (!valid)
throw new InvalidOperationException("This plane boundary is not valid.");
if (allocator == Allocator.Temp)
throw new InvalidOperationException("Allocator.Temp is not supported. Use Allocator.TempJob if you wish to use a temporary allocator.");
if (allocator == Allocator.None)
throw new InvalidOperationException("Allocator.None is not a valid allocator.");
TransformMLPolygon(*m_Boundary.polygon, m_Pose, allocator, ref polygonOut);
}
///
/// The number of vertices in this boundary's polygon.
///
public int polygonVertexCount { get; private set; }
///
/// Gets the polygon representing this boundary. The 2D vertices are in plane-space.
///
/// The allocator to use for the returned NativeArray. Must be Allocator.TempJob or Allocator.Persistent.
/// A new NativeArray containing a set of 2D points in plane-space representing a boundary for a plane.
/// The caller is responsible for disposing the NativeArray.
/// Thrown if is false.
/// Thrown if is Allocator.Temp or Allocator.None.
public NativeArray GetPolygon(Allocator allocator)
{
var polygon = new NativeArray();
GetPolygon(allocator, ref polygon);
return polygon;
}
///
/// The number of holes in this boundary.
///
public int holeCount
{
get
{
return (int)m_Boundary.holes_count;
}
}
///
/// Get the polygon representing a hole in this boundary. The 2D vertices are in plane-space.
///
/// The index of the hole. Must be less than .
/// The allocator to use for the returned NativeArray.
/// Must be Allocator.TempJob or Allocator.Persistent.
/// A new NativeArray allocated with containing a set of 2D vertices
/// in plane-space describing the hole at .
/// Thrown if is false.
/// Thrown if is Allocator.Temp or Allocator.None.
/// Thrown if is less than 0 or greater than or equal to .
public NativeArray GetHole(int index, Allocator allocator)
{
var hole = new NativeArray();
GetHole(index, allocator, ref hole);
return hole;
}
///
/// Get the polygon representing a hole in this boundary. The 2D vertices are in plane-space.
///
/// The index of the hole. Must be less than .
/// The allocator to use if must be resized.
/// Must be Allocator.TempJob or Allocator.Persistent.
/// The resulting polygon describing the hole at .
/// Thrown if is false.
/// Thrown if is Allocator.Temp or Allocator.None.
/// Thrown if is less than 0 or greater than or equal to .
public unsafe void GetHole(int index, Allocator allocator, ref NativeArray polygonOut)
{
if (!valid)
throw new InvalidOperationException("This plane boundary is not valid.");
if (allocator == Allocator.Temp)
throw new InvalidOperationException("Allocator.Temp is not supported. Use Allocator.TempJob if you wish to use a temporary allocator.");
if (allocator == Allocator.None)
throw new InvalidOperationException("Allocator.None is not a valid allocator.");
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), "Hole index must be greater than zero.");
if (index >= holeCount)
throw new ArgumentOutOfRangeException(nameof(index), $"Hole index must be less than or equal to holeCount ({holeCount}).");
var holes = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(
m_Boundary.holes,
holeCount,
Allocator.None);
#if UNITY_EDITOR
var safetyHandle = AtomicSafetyHandle.Create();
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref holes, safetyHandle);
#endif
TransformMLPolygon(holes[index], m_Pose, allocator, ref polygonOut);
}
///
/// Computes a hash code suitable for use in a Dictionary or HashSet.
///
/// A hash code suitable for use in a Dictionary or HashSet.
public override int GetHashCode()
{
unchecked
{
var hash = m_Boundary.GetHashCode();
hash = hash * 486187739 + m_Pose.GetHashCode();
return hash;
}
}
///
/// IEquatable interface. Compares for equality.
///
/// The object to compare for equality.
/// true if is of type and compares equal with .
public override bool Equals(object obj)
{
return ((obj is PlaneBoundary) && Equals((PlaneBoundary)obj));
}
///
/// IEquatable interface. Compares for equality.
///
/// The to compare against.
/// true if all fields of this compare equal to .
public bool Equals(PlaneBoundary other)
{
return
m_Boundary.Equals(other.m_Boundary) &&
m_Pose.Equals(other.m_Pose);
}
///
/// Compares for equality. Same as .
///
/// The left-hand side of the comparison.
/// The right-hand side of the comparison.
/// true if all fields of this compare equal to .
public static bool operator ==(PlaneBoundary lhs, PlaneBoundary rhs)
{
return lhs.Equals(rhs);
}
///
/// Compares for inequality. Same as !.
///
/// The left-hand side of the comparison.
/// The right-hand side of the comparison.
/// true if any of the fields of this are not equal to .
public static bool operator !=(PlaneBoundary lhs, PlaneBoundary rhs)
{
return !lhs.Equals(rhs);
}
static unsafe void TransformMLPolygon(Extensions.MLPolygon mlPolygon, Pose pose, Allocator allocator, ref NativeArray polygonOut)
{
var mlVertices = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(
mlPolygon.vertices,
(int)mlPolygon.vertices_count,
Allocator.None);
#if UNITY_EDITOR
var safetyHandle = AtomicSafetyHandle.Create();
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref mlVertices, safetyHandle);
#endif
CreateOrResizeNativeArrayIfNecessary(mlVertices.Length, allocator, ref polygonOut);
// The vertices are provided in session space, so we need to transform
// them into plane-space.
new TransformPlaneBoundaryJob
{
m_InvRotation = Quaternion.Inverse(CopyPlaneResultsJob.TransformUnityRotationToML(pose.rotation)),
m_Position = pose.position,
m_VerticesIn = mlVertices,
m_VerticesOut = polygonOut
}.Schedule(mlVertices.Length, 1).Complete();
}
internal unsafe PlaneBoundary(Extensions.MLPlaneBoundary planeBoundary, Pose pose)
{
m_Boundary = planeBoundary;
m_Pose = pose;
if (m_Boundary.polygon != null)
{
polygonVertexCount = (int)(*m_Boundary.polygon).vertices_count;
}
else
{
polygonVertexCount = 0;
}
}
static void CreateOrResizeNativeArrayIfNecessary(
int length,
Allocator allocator,
ref NativeArray array) where T : struct
{
if (array.IsCreated)
{
if (array.Length != length)
{
array.Dispose();
array = new NativeArray(length, allocator);
}
}
else
{
array = new NativeArray(length, allocator);
}
}
Extensions.MLPlaneBoundary m_Boundary;
Pose m_Pose;
}
}
}