// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Input; using Microsoft.MixedReality.Toolkit.Utilities; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.UI.PulseShader { /// /// Script for triggering the pulse shader effect on HoloLens 2 hand mesh. /// public class PulseShaderHandMeshHandler : PulseShaderHandler, IMixedRealityPointerHandler { [Space] [SerializeField] [Tooltip("Trigger the pulse shader visual if the palm of the left or right hand is facing the camera.")] private bool pulseOnLookAtPalms = false; /// /// Trigger the pulse shader visual if the palm of the left or right hand is facing the camera. /// public bool PulseOnLookAtPalms { get => pulseOnLookAtPalms; set => pulseOnLookAtPalms = value; } [SerializeField] [Tooltip("Trigger the pulse shader visual if the hand is in the pinch/select gesture. ")] private bool pulseFingerTipsOnPinch = false; /// /// Trigger the pulse shader visual if the hand is in the pinch/select gesture. /// public bool PulseFingerTipsOnPinch { get => pulseFingerTipsOnPinch; set => pulseFingerTipsOnPinch = value; } [SerializeField] [Tooltip("Trigger the pulse shader visual once if the left or right hand enters the frame and is tracked.")] private bool pulseOnHandDetected = true; /// /// Trigger the pulse shader visual once if the left or right hand enters the frame and is tracked. /// public bool PulseOnHandDetected { get => pulseOnHandDetected; set => pulseOnHandDetected = value; } [SerializeField] [Tooltip("The length of time required for the palm to face the camera before the pulse shader visual is triggered. ")] private float palmFacingTime = 0.25f; /// /// The length of time required for the palm to face the camera before the pulse shader visual is triggered. /// public float PalmFacingTime { get => palmFacingTime; set => palmFacingTime = value; } [SerializeField] [Tooltip("The local origin of the palms.")] private Vector3 pulseOriginPalms = new Vector3(0.5f, 0.5f, 0); /// /// The local origin of the palms. /// public Vector3 PulseOriginPalms { get => pulseOriginPalms; set => pulseOriginPalms = value; } [SerializeField] [Tooltip("The local origin of the pulse shader visual for the finger tips. ")] private Vector3 pulseOriginFingertips = new Vector3(0, 1f, 0); /// /// The local origin of the pulse shader visual for the finger tips. /// public Vector3 PulseOriginFingertips { get => pulseOriginFingertips; set => pulseOriginFingertips = value; } private bool pulseTriggeredLeft = false; private bool pulseTriggeredRight = false; private float palmFacingTimer = 0; protected override void Start() { base.Start(); CoreServices.InputSystem?.RegisterHandler(this); } protected override void OnDestroy() { base.OnDestroy(); CoreServices.InputSystem?.UnregisterHandler(this); } protected override void Update() { if (PulseOnLookAtPalms) { if (IsAPalmFacingCamera()) { if (palmFacingTimer >= 0) { palmFacingTimer += Time.deltaTime; if (palmFacingTimer > PalmFacingTime) { PulsePalms(); palmFacingTimer = -1; } } } else { palmFacingTimer = 0; } } if (PulseOnHandDetected) { // If joints are detected then an articulated hand is present bool isLeftHandDetected = HandJointUtils.TryGetJointPose(TrackedHandJoint.Palm, Handedness.Left, out MixedRealityPose leftPose); bool isRightHandDetected = HandJointUtils.TryGetJointPose(TrackedHandJoint.Palm, Handedness.Right, out MixedRealityPose rightPose); TriggerPulseOnHandDetected(isLeftHandDetected, isRightHandDetected); } } private void TriggerPulseOnHandDetected(bool isLeftHandDetected, bool isRightHandDetected) { // Left Hand if (isLeftHandDetected && !pulseTriggeredLeft) { pulseTriggeredLeft = true; PulsePalms(); } else if (!isLeftHandDetected && pulseTriggeredLeft) { pulseTriggeredLeft = false; } // Right Hand if (isRightHandDetected && !pulseTriggeredRight) { pulseTriggeredRight = true; PulsePalms(); } else if (!isRightHandDetected && pulseTriggeredRight) { pulseTriggeredRight = false; } } /// /// Set the local origin of the pulse shader visual to the palms and trigger the animation. /// public void PulsePalms() { SetLocalOrigin(PulseOriginPalms); PulseOnce(); } /// /// Set the local origin of the pulse shader visual to the finger tips and trigger the animation. /// public void PulseFingerTips() { SetLocalOrigin(PulseOriginFingertips); PulseOnce(); } // Check if the palm is facing the camera private bool IsAPalmFacingCamera() { foreach (IMixedRealityController controller in CoreServices.InputSystem.DetectedControllers) { if (controller.ControllerHandedness.IsMatch(Handedness.Both) && controller is IMixedRealityHand jointedHand && jointedHand.TryGetJoint(TrackedHandJoint.Palm, out MixedRealityPose palmPose) && Vector3.Dot(palmPose.Up, CameraCache.Main.transform.forward) > 0.0f) { return true; } } return false; } #region Pointer Handler public void OnPointerDown(MixedRealityPointerEventData eventData) { if (PulseFingerTipsOnPinch) { PulseFingerTips(); } } public void OnPointerDragged(MixedRealityPointerEventData eventData) { } public void OnPointerUp(MixedRealityPointerEventData eventData) { } public void OnPointerClicked(MixedRealityPointerEventData eventData) { } #endregion } }