251 lines
8.5 KiB
C#
251 lines
8.5 KiB
C#
using System;
|
|
using Unity.Collections;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using UnityEngine.XR.ARSubsystems;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace UnityEngine.XR.ARFoundation
|
|
{
|
|
/// <summary>
|
|
/// Represents a plane (that is, a flat surface) detected by an AR device.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Generated by the <see cref="ARPlaneManager"/> when an AR device detects
|
|
/// a plane in the environment.
|
|
///
|
|
/// Related information: <a href="xref:arfoundation-plane-arplane">AR Plane component</a>
|
|
/// </remarks>
|
|
[DefaultExecutionOrder(ARUpdateOrder.k_Plane)]
|
|
[DisallowMultipleComponent]
|
|
[HelpURL("features/plane-detection/arplane")]
|
|
public sealed class ARPlane : ARTrackable<BoundedPlane, ARPlane>
|
|
{
|
|
[SerializeField]
|
|
[Tooltip("The largest value by which a plane's vertex position may change before the boundaryChanged event is invoked. Units are in meters.")]
|
|
float m_VertexChangedThreshold = 0.01f;
|
|
|
|
NativeArray<Vector2> m_Boundary;
|
|
NativeArray<Vector2> m_OldBoundary;
|
|
bool m_HasBoundaryChanged;
|
|
|
|
/// <summary>
|
|
/// The largest value by which a plane's vertex could change before the mesh is regenerated. Units are in meters.
|
|
/// </summary>
|
|
public float vertexChangedThreshold
|
|
{
|
|
get => m_VertexChangedThreshold;
|
|
set => m_VertexChangedThreshold = Mathf.Max(0f, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when any vertex in the plane's boundary changes by more than <see cref="vertexChangedThreshold"/>.
|
|
/// </summary>
|
|
public event Action<ARPlaneBoundaryChangedEventArgs> boundaryChanged;
|
|
|
|
/// <summary>
|
|
/// Gets the normal to this plane in world space.
|
|
/// </summary>
|
|
public Vector3 normal => transform.up;
|
|
|
|
/// <summary>
|
|
/// The <see cref="ARPlane"/> which has subsumed this plane, or <c>null</c>
|
|
/// if this plane has not been subsumed.
|
|
/// </summary>
|
|
public ARPlane subsumedBy { get; internal set; }
|
|
|
|
/// <summary>
|
|
/// The alignment of this plane.
|
|
/// </summary>
|
|
public PlaneAlignment alignment => sessionRelativeData.alignment;
|
|
|
|
/// <summary>
|
|
/// The classification of this plane.
|
|
/// </summary>
|
|
[Obsolete("classification has been deprecated in AR Foundation 6.0. Use classifications instead.")]
|
|
public PlaneClassification classification
|
|
{
|
|
get
|
|
{
|
|
PlaneClassification classificationTemp = PlaneClassification.None;
|
|
classificationTemp.ConvertFromPlaneClassifications(sessionRelativeData.classifications);
|
|
return classificationTemp;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The classifications of this plane.
|
|
/// </summary>
|
|
public PlaneClassifications classifications => sessionRelativeData.classifications;
|
|
|
|
/// <summary>
|
|
/// The 2D center point, in plane space
|
|
/// </summary>
|
|
public Vector2 centerInPlaneSpace => sessionRelativeData.center;
|
|
|
|
/// <summary>
|
|
/// The 3D center point, in Unity world space.
|
|
/// </summary>
|
|
public Vector3 center => transform.TransformPoint(new Vector3(centerInPlaneSpace.x, 0, centerInPlaneSpace.y));
|
|
|
|
/// <summary>
|
|
/// The physical extents (half dimensions) of the plane in meters.
|
|
/// </summary>
|
|
public Vector2 extents => sessionRelativeData.extents;
|
|
|
|
/// <summary>
|
|
/// The physical size (dimensions) of the plane in meters.
|
|
/// </summary>
|
|
public Vector2 size => sessionRelativeData.size;
|
|
|
|
/// <summary>
|
|
/// Get the infinite plane associated with this <see cref="ARPlane"/>.
|
|
/// </summary>
|
|
public Plane infinitePlane => new Plane(normal, transform.position);
|
|
|
|
/// <summary>
|
|
/// The plane's boundary points, in plane space, that is, relative to this <see cref="ARPlane"/>'s
|
|
/// local position and rotation.
|
|
/// </summary>
|
|
public unsafe NativeArray<Vector2> boundary
|
|
{
|
|
get
|
|
{
|
|
if (!m_Boundary.IsCreated)
|
|
return default(NativeArray<Vector2>);
|
|
|
|
var boundary = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<Vector2>(
|
|
m_Boundary.GetUnsafePtr(),
|
|
m_Boundary.Length,
|
|
Allocator.None);
|
|
|
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
|
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(
|
|
ref boundary,
|
|
NativeArrayUnsafeUtility.GetAtomicSafetyHandle(m_Boundary));
|
|
#endif
|
|
|
|
return boundary;
|
|
}
|
|
}
|
|
|
|
internal void UpdateBoundary(XRPlaneSubsystem subsystem)
|
|
{
|
|
// subsystem cannot be null here
|
|
if (subsystem.subsystemDescriptor.supportsBoundaryVertices)
|
|
{
|
|
subsystem.GetBoundary(trackableId, Allocator.Persistent, ref m_Boundary);
|
|
}
|
|
else
|
|
{
|
|
if (!m_Boundary.IsCreated)
|
|
{
|
|
m_Boundary = new NativeArray<Vector2>(4, Allocator.Persistent);
|
|
}
|
|
else if (m_Boundary.Length != 4)
|
|
{
|
|
m_Boundary.Dispose();
|
|
m_Boundary = new NativeArray<Vector2>(4, Allocator.Persistent);
|
|
}
|
|
|
|
var extents = sessionRelativeData.extents;
|
|
m_Boundary[0] = new Vector2(-extents.x, -extents.y);
|
|
m_Boundary[1] = new Vector2(-extents.x, extents.y);
|
|
m_Boundary[2] = new Vector2(extents.x, extents.y);
|
|
m_Boundary[3] = new Vector2(extents.x, -extents.y);
|
|
}
|
|
|
|
if (boundaryChanged != null)
|
|
CheckForBoundaryChanges();
|
|
}
|
|
|
|
void OnValidate()
|
|
{
|
|
vertexChangedThreshold = Mathf.Max(0f, vertexChangedThreshold);
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
void Awake()
|
|
{
|
|
AssemblyReloadEvents.beforeAssemblyReload += DisposeNativeContainers;
|
|
}
|
|
#endif
|
|
|
|
void OnDestroy()
|
|
{
|
|
DisposeNativeContainers();
|
|
#if UNITY_EDITOR
|
|
AssemblyReloadEvents.beforeAssemblyReload -= DisposeNativeContainers;
|
|
#endif
|
|
}
|
|
|
|
void DisposeNativeContainers()
|
|
{
|
|
if (m_OldBoundary.IsCreated)
|
|
m_OldBoundary.Dispose();
|
|
if (m_Boundary.IsCreated)
|
|
m_Boundary.Dispose();
|
|
}
|
|
|
|
void CheckForBoundaryChanges()
|
|
{
|
|
if (m_Boundary.Length != m_OldBoundary.Length)
|
|
{
|
|
CopyBoundaryAndSetChangedFlag();
|
|
}
|
|
else if (vertexChangedThreshold == 0f)
|
|
{
|
|
// Don't need to check each vertex because it will always
|
|
// be "different" if threshold is zero.
|
|
CopyBoundaryAndSetChangedFlag();
|
|
}
|
|
else
|
|
{
|
|
// Counts are the same; check each vertex
|
|
var thresholdSquared = vertexChangedThreshold * vertexChangedThreshold;
|
|
for (int i = 0; i < m_Boundary.Length; ++i)
|
|
{
|
|
var diffSquared = (m_Boundary[i] - m_OldBoundary[i]).sqrMagnitude;
|
|
if (diffSquared > thresholdSquared)
|
|
{
|
|
CopyBoundaryAndSetChangedFlag();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CopyBoundaryAndSetChangedFlag()
|
|
{
|
|
// Copy new boundary
|
|
if (m_OldBoundary.IsCreated)
|
|
{
|
|
// If the lengths are different, then we need
|
|
// to reallocate, but otherwise, we can reuse
|
|
if (m_OldBoundary.Length != m_Boundary.Length)
|
|
{
|
|
m_OldBoundary.Dispose();
|
|
m_OldBoundary = new NativeArray<Vector2>(m_Boundary.Length, Allocator.Persistent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_OldBoundary = new NativeArray<Vector2>(m_Boundary.Length, Allocator.Persistent);
|
|
}
|
|
|
|
m_OldBoundary.CopyFrom(m_Boundary);
|
|
m_HasBoundaryChanged = true;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (m_HasBoundaryChanged && boundaryChanged != null)
|
|
{
|
|
m_HasBoundaryChanged = false;
|
|
boundaryChanged?.Invoke(new ARPlaneBoundaryChangedEventArgs(this));
|
|
}
|
|
}
|
|
}
|
|
}
|