- // Copyright (c) Microsoft Corporation. All rights reserved.
- // Licensed under the MIT License. See LICENSE in the project root for license information.
-
- using System;
- using System.Collections.Generic;
- using UnityEngine;
-
- #if UNITY_WSA && !UNITY_2017_2_OR_NEWER
- using UnityEngine.VR.WSA.Input;
- #endif
-
- namespace HoloToolkit.Unity.InputModule
- {
- /// <summary>
- /// Input source for raw interactions sources information, which gives finer details about current source state and position
- /// than the standard GestureRecognizer.
- /// This mostly allows users to access the source up/down and detected/lost events,
- /// which are not communicated as part of standard Windows gestures.
- /// </summary>
- /// <remarks>
- /// This input source only triggers SourceUp/SourceDown and SourceDetected/SourceLost.
- /// Everything else is handled by InteractionInputSource.
- /// </remarks>
- [Obsolete("Will be removed in a future release")]
- public class RawInteractionInputSource : BaseInputSource
- {
- /// <summary>
- /// Data for an interaction source.
- /// </summary>
- private class SourceData
- {
- public SourceData(uint sourceId)
- {
- SourceId = sourceId;
- HasPosition = false;
- SourcePosition = Vector3.zero;
- IsSourceDown = false;
- IsSourceDownPending = false;
- SourceStateChanged = false;
- SourceStateUpdateTimer = -1;
- }
-
- public readonly uint SourceId;
- public bool HasPosition;
- public Vector3 SourcePosition;
- public bool IsSourceDown;
- public bool IsSourceDownPending;
- public bool SourceStateChanged;
- public float SourceStateUpdateTimer;
- }
-
- /// <summary>
- /// Delay before a source press is considered.
- /// This mitigates fake source taps that can sometimes be detected while the input source is moving.
- /// </summary>
- private const float SourcePressDelay = 0.07f;
-
- [Tooltip("Use unscaled time. This is useful for games that have a pause mechanism or otherwise adjust the game timescale.")]
- public bool UseUnscaledTime = true;
-
- /// <summary>
- /// Dictionary linking each source ID to its data.
- /// </summary>
- private readonly Dictionary<uint, SourceData> sourceIdToData = new Dictionary<uint, SourceData>(4);
- private readonly List<uint> pendingSourceIdDeletes = new List<uint>();
-
- // HashSets used to be able to quickly update the sources data when they become visible / not visible
- private readonly HashSet<uint> currentSources = new HashSet<uint>();
- private readonly HashSet<uint> newSources = new HashSet<uint>();
-
- public override SupportedInputInfo GetSupportedInputInfo(uint sourceId)
- {
- SupportedInputInfo retVal = SupportedInputInfo.None;
-
- SourceData sourceData;
- if (sourceIdToData.TryGetValue(sourceId, out sourceData))
- {
- if (sourceData.HasPosition)
- {
- retVal |= SupportedInputInfo.Position;
- }
- }
-
- return retVal;
- }
-
- public override bool TryGetMenu(uint sourceId, out bool isPressed)
- {
- throw new NotImplementedException();
- }
-
- public override bool TryGetPointerPosition(uint sourceId, out Vector3 position)
- {
- SourceData sourceData;
- if (sourceIdToData.TryGetValue(sourceId, out sourceData))
- {
- if (sourceData.HasPosition)
- {
- position = sourceData.SourcePosition;
- return true;
- }
- }
-
- // Else, the source doesn't have positional information
- position = Vector3.zero;
- return false;
- }
-
- public override bool TryGetPointerRotation(uint sourceId, out Quaternion orientation)
- {
- // Orientation is not supported by any Windows interaction sources
- orientation = Quaternion.identity;
- return false;
- }
-
- public override bool TryGetSourceKind(uint sourceId, out InteractionSourceInfo sourceKind)
- {
- throw new NotImplementedException();
- }
-
- public override bool TryGetGripPosition(uint sourceId, out Vector3 position)
- {
- throw new NotImplementedException();
- }
-
- public override bool TryGetGripRotation(uint sourceId, out Quaternion rotation)
- {
- throw new NotImplementedException();
- }
-
- public override bool TryGetThumbstick(uint sourceId, out bool isPressed, out Vector2 position)
- {
- throw new NotImplementedException();
- }
-
- public override bool TryGetTouchpad(uint sourceId, out bool isPressed, out bool isTouched, out Vector2 position)
- {
- throw new NotImplementedException();
- }
-
- public override bool TryGetSelect(uint sourceId, out bool isPressed, out double pressedValue)
- {
- throw new NotImplementedException();
- }
-
- public override bool TryGetGrasp(uint sourceId, out bool isPressed)
- {
- throw new NotImplementedException();
- }
-
- public override bool TryGetPointingRay(uint sourceId, out Ray pointingRay)
- {
- throw new NotImplementedException();
- }
-
- private void Update()
- {
- newSources.Clear();
- currentSources.Clear();
-
- UpdateSourceData();
- SendSourceVisibilityEvents();
- }
-
- /// <summary>
- /// Update the source data for the currently detected sources.
- /// </summary>
- private void UpdateSourceData()
- {
- #if UNITY_WSA && !UNITY_2017_2_OR_NEWER
- // Poll for updated reading from hands
- InteractionSourceState[] sourceStates = InteractionManager.GetCurrentReading();
- if (sourceStates != null)
- {
- for (var i = 0; i < sourceStates.Length; ++i)
- {
- InteractionSourceState handSource = sourceStates[i];
- SourceData sourceData = GetOrAddSourceData(handSource.source);
- currentSources.Add(handSource.source.id);
-
- UpdateSourceState(handSource, sourceData);
- }
- }
- #endif
- }
-
- #if UNITY_WSA && !UNITY_2017_2_OR_NEWER
- /// <summary>
- /// Gets the source data for the specified interaction source if it already exists, otherwise creates it.
- /// </summary>
- /// <param name="interactionSource">Interaction source for which data should be retrieved.</param>
- /// <returns>The source data requested.</returns>
- private SourceData GetOrAddSourceData(InteractionSource interactionSource)
- {
- SourceData sourceData;
- if (!sourceIdToData.TryGetValue(interactionSource.id, out sourceData))
- {
- sourceData = new SourceData(interactionSource.id);
- sourceIdToData.Add(sourceData.SourceId, sourceData);
- newSources.Add(sourceData.SourceId);
- }
-
- return sourceData;
- }
-
- /// <summary>
- /// Updates the source positional information.
- /// </summary>
- /// <param name="interactionSource">Interaction source to use to update the position.</param>
- /// <param name="sourceData">SourceData structure to update.</param>
- private void UpdateSourceState(InteractionSourceState interactionSource, SourceData sourceData)
- {
- // Update source position
- Vector3 sourcePosition;
- if (interactionSource.properties.location.TryGetPosition(out sourcePosition))
- {
- sourceData.HasPosition = true;
- sourceData.SourcePosition = sourcePosition;
- }
-
- // Check for source presses
- if (interactionSource.pressed != sourceData.IsSourceDownPending)
- {
- sourceData.IsSourceDownPending = interactionSource.pressed;
- sourceData.SourceStateUpdateTimer = SourcePressDelay;
- }
-
- // Source presses are delayed to mitigate issue with hand position shifting during air tap
- sourceData.SourceStateChanged = false;
- if (sourceData.SourceStateUpdateTimer >= 0)
- {
- float deltaTime = UseUnscaledTime
- ? Time.unscaledDeltaTime
- : Time.deltaTime;
-
- sourceData.SourceStateUpdateTimer -= deltaTime;
- if (sourceData.SourceStateUpdateTimer < 0)
- {
- sourceData.IsSourceDown = sourceData.IsSourceDownPending;
- sourceData.SourceStateChanged = true;
- }
- }
-
- SendSourceStateEvents(sourceData);
- }
- #endif
-
- /// <summary>
- /// Sends the events for source state changes.
- /// </summary>
- /// <param name="sourceData">Source data for which events should be sent.</param>
- private void SendSourceStateEvents(SourceData sourceData)
- {
- // Source pressed/released events
- if (sourceData.SourceStateChanged)
- {
- if (sourceData.IsSourceDown)
- {
- InputManager.Instance.RaiseSourceDown(this, sourceData.SourceId, InteractionSourcePressInfo.Select);
- }
- else
- {
- InputManager.Instance.RaiseSourceUp(this, sourceData.SourceId, InteractionSourcePressInfo.Select);
- }
- }
- }
-
- /// <summary>
- /// Sends the events for source visibility changes.
- /// </summary>
- private void SendSourceVisibilityEvents()
- {
- // Send event for new sources that were added
- foreach (uint newSource in newSources)
- {
- InputManager.Instance.RaiseSourceDetected(this, newSource);
- }
-
- // Send event for sources that are no longer visible and remove them from our dictionary
- foreach (uint existingSource in sourceIdToData.Keys)
- {
- if (!currentSources.Contains(existingSource))
- {
- pendingSourceIdDeletes.Add(existingSource);
- InputManager.Instance.RaiseSourceLost(this, existingSource);
- }
- }
-
- // Remove pending source IDs
- for (int i = 0; i < pendingSourceIdDeletes.Count; ++i)
- {
- sourceIdToData.Remove(pendingSourceIdDeletes[i]);
- }
- pendingSourceIdDeletes.Clear();
- }
- }
- }