// %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; } } }