// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities; using Unity.Profiling; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Input { [AddComponentMenu("Scripts/MRTK/SDK/SpherePointerVisual")] public class SpherePointerVisual : MonoBehaviour { public Transform TetherEndPoint => tetherEndPoint; public bool TetherVisualsEnabled { get; private set; } [Tooltip("The pointer these visuals decorate")] private SpherePointer pointer; [SerializeField] [Tooltip("Tether will not be shown unless it is at least this long")] private float minTetherLength = 0.03f; [SerializeField] private Transform visualsRoot = null; [SerializeField] private Transform tetherEndPoint = null; /// Assumption: Tether line is a child of the visuals! [SerializeField] private BaseMixedRealityLineDataProvider tetherLine = null; public void OnEnable() { CheckInitialization(); } public void OnDestroy() { if (visualsRoot != null) { Destroy(visualsRoot.gameObject); } } public void Start() { // put it at root of scene MixedRealityPlayspace.AddChild(visualsRoot.transform); visualsRoot.gameObject.name = $"{gameObject.name}_NearTetherVisualsRoot"; } private void CheckInitialization() { if (pointer == null) { pointer = GetComponent(); } if (pointer == null) { Debug.LogError($"No SpherePointer found on {gameObject.name}."); } CheckAsset(visualsRoot, "Visuals Root"); CheckAsset(tetherEndPoint, "Tether End Point"); CheckAsset(tetherLine, "Tether Line"); } private void CheckAsset(object asset, string fieldname) { if (asset == null) { Debug.LogError($"No {fieldname} specified on {gameObject.name}.SpherePointerVisual. Did you forget to set the {fieldname}?"); } } private static readonly ProfilerMarker UpdatePerfMarker = new ProfilerMarker("[MRTK] SpherePointerVisual.Update"); public void Update() { using (UpdatePerfMarker.Auto()) { TetherVisualsEnabled = false; if (pointer.IsFocusLocked && pointer.IsTargetPositionLockedOnFocusLock && pointer.Result != null) { NearInteractionGrabbable grabbedObject = GetGrabbedObject(); if (grabbedObject != null && grabbedObject.ShowTetherWhenManipulating) { Vector3 graspPosition; pointer.TryGetNearGraspPoint(out graspPosition); tetherLine.FirstPoint = graspPosition; Vector3 endPoint = pointer.Result.Details.Object.transform.TransformPoint(pointer.Result.Details.PointLocalSpace); tetherLine.LastPoint = endPoint; TetherVisualsEnabled = Vector3.Distance(tetherLine.FirstPoint, tetherLine.LastPoint) > minTetherLength; tetherLine.enabled = TetherVisualsEnabled; tetherEndPoint.gameObject.SetActive(TetherVisualsEnabled); tetherEndPoint.position = endPoint; } } visualsRoot.gameObject.SetActive(TetherVisualsEnabled); } } private static readonly ProfilerMarker GetGrabbedObjectPerfMarker = new ProfilerMarker("[MRTK] SpherePointer.GetGrabbedObject"); private NearInteractionGrabbable GetGrabbedObject() { using (GetGrabbedObjectPerfMarker.Auto()) { if (pointer.Result?.Details.Object != null) { return pointer.Result.Details.Object.GetComponent(); } else { return null; } } } } }