using System; using UnityEngine.XR.ARSubsystems; namespace UnityEngine.XR.ARFoundation { /// /// Generates a mesh for an . /// /// /// If this component's [GameObject](xref:UnityEngine.GameObject) has a [MeshFilter](xref:UnityEngine.MeshFilter) /// and/or a [MeshCollider](xref:UnityEngine.MeshCollider), this component will generate a mesh from the /// [BoundedPlane](xref:UnityEngine.XR.ARSubsystems.BoundedPlane) associated with the . /// /// It will also update a [LineRenderer](xref:UnityEngine.LineRenderer) with the boundary points, if present. /// /// [MeshRenderer](xref:UnityEngine.MeshRenderer) and [LineRenderer](xref:UnityEngine.LineRenderer) components will only be /// enabled if: /// - This component is enabled. /// - The plane's is greater than /// or equal to . /// - The 's is greater than . /// - is `false` OR is not `null`. /// /// Related information: Custom plane visualizers /// [RequireComponent(typeof(ARPlane))] [HelpURL("features/plane-detection/arplanemanager", "custom-plane-visualizers")] public sealed class ARPlaneMeshVisualizer : MonoBehaviour { /// /// Get the [Mesh](xref:UnityEngine.Mesh) that this visualizer creates and manages. /// public Mesh mesh { get; private set; } [SerializeField] [Tooltip("A plane whose tracking state is less than this threshold will have its render mesh and line renderer components disabled.")] TrackingState m_TrackingStateVisibilityThreshold = TrackingState.Limited; /// /// The threshold [TrackingState](xref:UnityEngine.XR.ARSubsystems.TrackingState) that affects the visibility of /// the [MeshRenderer](xref:UnityEngine.MeshRenderer) and [LineRenderer](xref:UnityEngine.LineRenderer) components. /// /// /// [MeshRenderer](xref:UnityEngine.MeshRenderer) and [LineRenderer](xref:UnityEngine.LineRenderer) components will only be /// enabled if: /// - This component is enabled. /// - The plane's is greater than /// or equal to . /// - The 's is greater than . /// - is `false` OR is not `null`. /// public TrackingState trackingStateVisibilityThreshold { get => m_TrackingStateVisibilityThreshold; set { m_TrackingStateVisibilityThreshold = value; Debug.Log($"Setting visibility threshold of {GetComponent().trackableId} to {m_TrackingStateVisibilityThreshold}. Tracking state is {GetComponent().trackingState}."); UpdateVisibility(); } } [SerializeField] [Tooltip("When enabled, a plane that has been subsumed by (i.e., merged into) another plane will have its render mesh and line renderer disabled.")] bool m_HideSubsumed = true; /// /// Indicates whether subsumed planes should be rendered. (See .) /// /// /// [MeshRenderer](xref:UnityEngine.MeshRenderer) and [LineRenderer](xref:UnityEngine.LineRenderer) components will only be /// enabled if: /// - This component is enabled. /// - The plane's is greater than /// or equal to . /// - The 's is greater than . /// - is `false` OR is not `null`. /// public bool hideSubsumed { get => m_HideSubsumed; set { m_HideSubsumed = value; UpdateVisibility(); } } void OnBoundaryChanged(ARPlaneBoundaryChangedEventArgs eventArgs) { var boundary = m_Plane.boundary; if (!ARPlaneMeshGenerator.TryGenerateMesh(mesh, boundary)) return; var lineRenderer = GetComponent(); if (lineRenderer != null) { lineRenderer.positionCount = boundary.Length; for (int i = 0; i < boundary.Length; ++i) { var point2 = boundary[i]; lineRenderer.SetPosition(i, new Vector3(point2.x, 0, point2.y)); } } var meshFilter = GetComponent(); if (meshFilter != null) meshFilter.sharedMesh = mesh; var meshCollider = GetComponent(); if (meshCollider != null) meshCollider.sharedMesh = mesh; } void DisableComponents() { enabled = false; var meshCollider = GetComponent(); if (meshCollider != null) meshCollider.enabled = false; UpdateVisibility(); } void SetRendererEnabled(bool visible) where T : Renderer { var component = GetComponent(); if (component) { component.enabled = visible; } } void SetVisible(bool visible) { SetRendererEnabled(visible); SetRendererEnabled(visible); } void UpdateVisibility() { var visible = enabled && m_Plane.trackingState >= m_TrackingStateVisibilityThreshold && ARSession.state > ARSessionState.Ready && (!m_HideSubsumed || m_Plane.subsumedBy == null); SetVisible(visible); } void Awake() { mesh = new Mesh(); m_Plane = GetComponent(); } void OnEnable() { m_Plane.boundaryChanged += OnBoundaryChanged; UpdateVisibility(); OnBoundaryChanged(default(ARPlaneBoundaryChangedEventArgs)); } void OnDisable() { m_Plane.boundaryChanged -= OnBoundaryChanged; UpdateVisibility(); } void Update() { if (transform.hasChanged) { var lineRenderer = GetComponent(); if (lineRenderer != null) { if (!m_InitialLineWidthMultiplier.HasValue) m_InitialLineWidthMultiplier = lineRenderer.widthMultiplier; lineRenderer.widthMultiplier = m_InitialLineWidthMultiplier.Value * transform.lossyScale.x; } else { m_InitialLineWidthMultiplier = null; } transform.hasChanged = false; } if (m_Plane.subsumedBy != null) { DisableComponents(); } else { UpdateVisibility(); } } float? m_InitialLineWidthMultiplier; ARPlane m_Plane; } }