123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668 |
- using System;
- using UnityEngine.InputSystem.LowLevel;
-
- namespace UnityEngine.InputSystem.XR
- {
- /// <summary>
- /// The <see cref="TrackedPoseDriver"/> component applies the current pose value of a tracked device
- /// to the <see cref="Transform"/> of the <see cref="GameObject"/>.
- /// <see cref="TrackedPoseDriver"/> can track multiple types of devices including XR HMDs, controllers, and remotes.
- /// </summary>
- /// <remarks>
- /// For <see cref="positionInput"/> and <see cref="rotationInput"/>, if an action is directly defined
- /// in the <see cref="InputActionProperty"/>, as opposed to a reference to an action externally defined
- /// in an <see cref="InputActionAsset"/>, the action will automatically be enabled and disabled by this
- /// behavior during <see cref="OnEnable"/> and <see cref="OnDisable"/>. The enabled state for actions
- /// externally defined must be managed externally from this behavior.
- /// </remarks>
- [Serializable]
- [AddComponentMenu("XR/Tracked Pose Driver (Input System)")]
- public class TrackedPoseDriver : MonoBehaviour, ISerializationCallbackReceiver
- {
- /// <summary>
- /// Options for which <see cref="Transform"/> properties to update.
- /// </summary>
- /// <seealso cref="trackingType"/>
- public enum TrackingType
- {
- /// <summary>
- /// Update both rotation and position.
- /// </summary>
- RotationAndPosition,
-
- /// <summary>
- /// Update rotation only.
- /// </summary>
- RotationOnly,
-
- /// <summary>
- /// Update position only.
- /// </summary>
- PositionOnly,
- }
-
- /// <summary>
- /// These bit flags correspond with <c>UnityEngine.XR.InputTrackingState</c>
- /// but that enum is not used to avoid adding a dependency to the XR module.
- /// Only the Position and Rotation flags are used by this class, so velocity and acceleration flags are not duplicated here.
- /// </summary>
- [Flags]
- enum TrackingStates
- {
- /// <summary>
- /// Position and rotation are not valid.
- /// </summary>
- None,
-
- /// <summary>
- /// Position is valid.
- /// See <c>InputTrackingState.Position</c>.
- /// </summary>
- Position = 1 << 0,
-
- /// <summary>
- /// Rotation is valid.
- /// See <c>InputTrackingState.Rotation</c>.
- /// </summary>
- Rotation = 1 << 1,
- }
-
- [SerializeField, Tooltip("Which Transform properties to update.")]
- TrackingType m_TrackingType;
- /// <summary>
- /// The tracking type being used by the Tracked Pose Driver
- /// to control which <see cref="Transform"/> properties to update.
- /// </summary>
- /// <seealso cref="TrackingType"/>
- public TrackingType trackingType
- {
- get => m_TrackingType;
- set => m_TrackingType = value;
- }
-
- /// <summary>
- /// Options for which phases of the player loop will update <see cref="Transform"/> properties.
- /// </summary>
- /// <seealso cref="updateType"/>
- /// <seealso cref="InputSystem.onAfterUpdate"/>
- public enum UpdateType
- {
- /// <summary>
- /// Update after the Input System has completed an update and right before rendering.
- /// This is the recommended and default option to minimize lag for XR tracked devices.
- /// </summary>
- /// <seealso cref="InputUpdateType.BeforeRender"/>
- UpdateAndBeforeRender,
-
- /// <summary>
- /// Update after the Input System has completed an update except right before rendering.
- /// </summary>
- /// <remarks>
- /// This may be dynamic update, fixed update, or a manual update depending on the Update Mode
- /// project setting for Input System.
- /// </remarks>
- Update,
-
- /// <summary>
- /// Update after the Input System has completed an update right before rendering.
- /// </summary>
- /// <remarks>
- /// Note that this update mode may not trigger if there are no XR devices added which use before render timing.
- /// </remarks>
- /// <seealso cref="InputUpdateType.BeforeRender"/>
- /// <seealso cref="InputDevice.updateBeforeRender"/>
- BeforeRender,
- }
-
- [SerializeField, Tooltip("Updates the Transform properties after these phases of Input System event processing.")]
- UpdateType m_UpdateType = UpdateType.UpdateAndBeforeRender;
- /// <summary>
- /// The update type being used by the Tracked Pose Driver
- /// to control which phases of the player loop will update <see cref="Transform"/> properties.
- /// </summary>
- /// <seealso cref="UpdateType"/>
- public UpdateType updateType
- {
- get => m_UpdateType;
- set => m_UpdateType = value;
- }
-
- [SerializeField, Tooltip("Ignore Tracking State and always treat the input pose as valid.")]
- bool m_IgnoreTrackingState;
- /// <summary>
- /// Ignore tracking state and always treat the input pose as valid when updating the <see cref="Transform"/> properties.
- /// The recommended value is <see langword="false"/> so the tracking state input is used.
- /// </summary>
- /// <seealso cref="trackingStateInput"/>
- public bool ignoreTrackingState
- {
- get => m_IgnoreTrackingState;
- set => m_IgnoreTrackingState = value;
- }
-
- [SerializeField, Tooltip("The input action to read the position value of a tracked device. Must be a Vector 3 control type.")]
- InputActionProperty m_PositionInput;
- /// <summary>
- /// The input action to read the position value of a tracked device.
- /// Must support reading a value of type <see cref="Vector3"/>.
- /// </summary>
- /// <seealso cref="rotationInput"/>
- public InputActionProperty positionInput
- {
- get => m_PositionInput;
- set
- {
- if (Application.isPlaying)
- UnbindPosition();
-
- m_PositionInput = value;
-
- if (Application.isPlaying && isActiveAndEnabled)
- BindPosition();
- }
- }
-
- [SerializeField, Tooltip("The input action to read the rotation value of a tracked device. Must be a Quaternion control type.")]
- InputActionProperty m_RotationInput;
- /// <summary>
- /// The input action to read the rotation value of a tracked device.
- /// Must support reading a value of type <see cref="Quaternion"/>.
- /// </summary>
- /// <seealso cref="positionInput"/>
- public InputActionProperty rotationInput
- {
- get => m_RotationInput;
- set
- {
- if (Application.isPlaying)
- UnbindRotation();
-
- m_RotationInput = value;
-
- if (Application.isPlaying && isActiveAndEnabled)
- BindRotation();
- }
- }
-
- [SerializeField, Tooltip("The input action to read the tracking state value of a tracked device. Identifies if position and rotation have valid data. Must be an Integer control type.")]
- InputActionProperty m_TrackingStateInput;
- /// <summary>
- /// The input action to read the tracking state value of a tracked device.
- /// Identifies if position and rotation have valid data.
- /// Must support reading a value of type <see cref="int"/>.
- /// </summary>
- /// <remarks>
- /// See [InputTrackingState](xref:UnityEngine.XR.InputTrackingState) enum for values the input action represents.
- /// <list type="bullet">
- /// <item>
- /// <term>[InputTrackingState.None](xref:UnityEngine.XR.InputTrackingState.None) (0)</term>
- /// <description>to indicate neither position nor rotation is valid.</description>
- /// </item>
- /// <item>
- /// <term>[InputTrackingState.Position](xref:UnityEngine.XR.InputTrackingState.Position) (1)</term>
- /// <description>to indicate position is valid.</description>
- /// </item>
- /// <item>
- /// <term>[InputTrackingState.Rotation](xref:UnityEngine.XR.InputTrackingState.Rotation) (2)</term>
- /// <description>to indicate rotation is valid.</description>
- /// </item>
- /// <item>
- /// <term>[InputTrackingState.Position](xref:UnityEngine.XR.InputTrackingState.Position) <c>|</c> [InputTrackingState.Rotation](xref:UnityEngine.XR.InputTrackingState.Rotation) (3)</term>
- /// <description>to indicate position and rotation is valid.</description>
- /// </item>
- /// </list>
- /// </remarks>
- /// <seealso cref="ignoreTrackingState"/>
- public InputActionProperty trackingStateInput
- {
- get => m_TrackingStateInput;
- set
- {
- if (Application.isPlaying)
- UnbindTrackingState();
-
- m_TrackingStateInput = value;
-
- if (Application.isPlaying && isActiveAndEnabled)
- BindTrackingState();
- }
- }
-
- Vector3 m_CurrentPosition = Vector3.zero;
- Quaternion m_CurrentRotation = Quaternion.identity;
- TrackingStates m_CurrentTrackingState = TrackingStates.Position | TrackingStates.Rotation;
- bool m_RotationBound;
- bool m_PositionBound;
- bool m_TrackingStateBound;
- bool m_IsFirstUpdate = true;
-
- void BindActions()
- {
- BindPosition();
- BindRotation();
- BindTrackingState();
- }
-
- void UnbindActions()
- {
- UnbindPosition();
- UnbindRotation();
- UnbindTrackingState();
- }
-
- void BindPosition()
- {
- if (m_PositionBound)
- return;
-
- var action = m_PositionInput.action;
- if (action == null)
- return;
-
- action.performed += OnPositionPerformed;
- action.canceled += OnPositionCanceled;
- m_PositionBound = true;
-
- if (m_PositionInput.reference == null)
- {
- action.Rename($"{gameObject.name} - TPD - Position");
- action.Enable();
- }
- }
-
- void BindRotation()
- {
- if (m_RotationBound)
- return;
-
- var action = m_RotationInput.action;
- if (action == null)
- return;
-
- action.performed += OnRotationPerformed;
- action.canceled += OnRotationCanceled;
- m_RotationBound = true;
-
- if (m_RotationInput.reference == null)
- {
- action.Rename($"{gameObject.name} - TPD - Rotation");
- action.Enable();
- }
- }
-
- void BindTrackingState()
- {
- if (m_TrackingStateBound)
- return;
-
- var action = m_TrackingStateInput.action;
- if (action == null)
- return;
-
- action.performed += OnTrackingStatePerformed;
- action.canceled += OnTrackingStateCanceled;
- m_TrackingStateBound = true;
-
- if (m_TrackingStateInput.reference == null)
- {
- action.Rename($"{gameObject.name} - TPD - Tracking State");
- action.Enable();
- }
- }
-
- void UnbindPosition()
- {
- if (!m_PositionBound)
- return;
-
- var action = m_PositionInput.action;
- if (action == null)
- return;
-
- if (m_PositionInput.reference == null)
- action.Disable();
-
- action.performed -= OnPositionPerformed;
- action.canceled -= OnPositionCanceled;
- m_PositionBound = false;
- }
-
- void UnbindRotation()
- {
- if (!m_RotationBound)
- return;
-
- var action = m_RotationInput.action;
- if (action == null)
- return;
-
- if (m_RotationInput.reference == null)
- action.Disable();
-
- action.performed -= OnRotationPerformed;
- action.canceled -= OnRotationCanceled;
- m_RotationBound = false;
- }
-
- void UnbindTrackingState()
- {
- if (!m_TrackingStateBound)
- return;
-
- var action = m_TrackingStateInput.action;
- if (action == null)
- return;
-
- if (m_TrackingStateInput.reference == null)
- action.Disable();
-
- action.performed -= OnTrackingStatePerformed;
- action.canceled -= OnTrackingStateCanceled;
- m_TrackingStateBound = false;
- }
-
- void OnPositionPerformed(InputAction.CallbackContext context)
- {
- m_CurrentPosition = context.ReadValue<Vector3>();
- }
-
- void OnPositionCanceled(InputAction.CallbackContext context)
- {
- m_CurrentPosition = Vector3.zero;
- }
-
- void OnRotationPerformed(InputAction.CallbackContext context)
- {
- m_CurrentRotation = context.ReadValue<Quaternion>();
- }
-
- void OnRotationCanceled(InputAction.CallbackContext context)
- {
- m_CurrentRotation = Quaternion.identity;
- }
-
- void OnTrackingStatePerformed(InputAction.CallbackContext context)
- {
- m_CurrentTrackingState = (TrackingStates)context.ReadValue<int>();
- }
-
- void OnTrackingStateCanceled(InputAction.CallbackContext context)
- {
- m_CurrentTrackingState = TrackingStates.None;
- }
-
- /// <summary>
- /// This function is called when the user hits the Reset button in the Inspector's context menu
- /// or when adding the component the first time. This function is only called in editor mode.
- /// </summary>
- protected void Reset()
- {
- m_PositionInput = new InputActionProperty(new InputAction("Position", expectedControlType: "Vector3"));
- m_RotationInput = new InputActionProperty(new InputAction("Rotation", expectedControlType: "Quaternion"));
- m_TrackingStateInput = new InputActionProperty(new InputAction("Tracking State", expectedControlType: "Integer"));
- }
-
- /// <summary>
- /// This function is called when the script instance is being loaded.
- /// </summary>
- protected virtual void Awake()
- {
- #if UNITY_INPUT_SYSTEM_ENABLE_VR && ENABLE_VR
- if (HasStereoCamera(out var cameraComponent))
- {
- UnityEngine.XR.XRDevice.DisableAutoXRCameraTracking(cameraComponent, true);
- }
- #endif
- }
-
- /// <summary>
- /// This function is called when the object becomes enabled and active.
- /// </summary>
- protected void OnEnable()
- {
- InputSystem.onAfterUpdate += UpdateCallback;
- BindActions();
-
- // Read current input values when becoming enabled,
- // but wait until after the input update so the input is read at a consistent time
- m_IsFirstUpdate = true;
- }
-
- /// <summary>
- /// This function is called when the object becomes disabled or inactive.
- /// </summary>
- protected void OnDisable()
- {
- UnbindActions();
- InputSystem.onAfterUpdate -= UpdateCallback;
- }
-
- /// <summary>
- /// This function is called when the <see cref="MonoBehaviour"/> will be destroyed.
- /// </summary>
- protected virtual void OnDestroy()
- {
- #if UNITY_INPUT_SYSTEM_ENABLE_VR && ENABLE_VR
- if (HasStereoCamera(out var cameraComponent))
- {
- UnityEngine.XR.XRDevice.DisableAutoXRCameraTracking(cameraComponent, false);
- }
- #endif
- }
-
- /// <summary>
- /// The callback method called after the Input System has completed an update and processed all pending events.
- /// </summary>
- /// <seealso cref="InputSystem.onAfterUpdate"/>
- protected void UpdateCallback()
- {
- if (m_IsFirstUpdate)
- {
- // Update current input values if this is the first update since becoming enabled
- // since the performed callbacks may not have been executed
- if (m_PositionInput.action != null)
- m_CurrentPosition = m_PositionInput.action.ReadValue<Vector3>();
-
- if (m_RotationInput.action != null)
- m_CurrentRotation = m_RotationInput.action.ReadValue<Quaternion>();
-
- ReadTrackingState();
-
- m_IsFirstUpdate = false;
- }
-
- if (InputState.currentUpdateType == InputUpdateType.BeforeRender)
- OnBeforeRender();
- else
- OnUpdate();
- }
-
- void ReadTrackingState()
- {
- var trackingStateAction = m_TrackingStateInput.action;
- if (trackingStateAction != null && !trackingStateAction.enabled)
- {
- // Treat a disabled action as the default None value for the ReadValue call
- m_CurrentTrackingState = TrackingStates.None;
- return;
- }
-
- if (trackingStateAction == null || trackingStateAction.m_BindingsCount == 0)
- {
- // Treat an Input Action Reference with no reference the same as
- // an enabled Input Action with no authored bindings, and allow driving the Transform pose.
- m_CurrentTrackingState = TrackingStates.Position | TrackingStates.Rotation;
- return;
- }
-
- // Grab state.
- var actionMap = trackingStateAction.GetOrCreateActionMap();
- actionMap.ResolveBindingsIfNecessary();
- var state = actionMap.m_State;
-
- // Get list of resolved controls to determine if a device actually has tracking state.
- var hasResolvedControl = false;
- if (state != null)
- {
- var actionIndex = trackingStateAction.m_ActionIndexInState;
- var totalBindingCount = state.totalBindingCount;
- for (var i = 0; i < totalBindingCount; ++i)
- {
- unsafe
- {
- ref var bindingState = ref state.bindingStates[i];
- if (bindingState.actionIndex != actionIndex)
- continue;
- if (bindingState.isComposite)
- continue;
-
- if (bindingState.controlCount > 0)
- {
- hasResolvedControl = true;
- break;
- }
- }
- }
- }
-
- // Retain the current value if there is no resolved binding.
- // Since the field initializes to allowing position and rotation,
- // this allows for driving the Transform pose always when the device
- // doesn't support reporting the tracking state.
- if (hasResolvedControl)
- m_CurrentTrackingState = (TrackingStates)trackingStateAction.ReadValue<int>();
- }
-
- /// <summary>
- /// This method is called after the Input System has completed an update and processed all pending events
- /// when the type of update is not <see cref="InputUpdateType.BeforeRender"/>.
- /// </summary>
- protected virtual void OnUpdate()
- {
- if (m_UpdateType == UpdateType.Update ||
- m_UpdateType == UpdateType.UpdateAndBeforeRender)
- {
- PerformUpdate();
- }
- }
-
- /// <summary>
- /// This method is called after the Input System has completed an update and processed all pending events
- /// when the type of update is <see cref="InputUpdateType.BeforeRender"/>.
- /// </summary>
- protected virtual void OnBeforeRender()
- {
- if (m_UpdateType == UpdateType.BeforeRender ||
- m_UpdateType == UpdateType.UpdateAndBeforeRender)
- {
- PerformUpdate();
- }
- }
-
- /// <summary>
- /// Updates <see cref="Transform"/> properties with the current input pose values that have been read,
- /// constrained by tracking type and tracking state.
- /// </summary>
- /// <seealso cref="SetLocalTransform"/>
- protected virtual void PerformUpdate()
- {
- SetLocalTransform(m_CurrentPosition, m_CurrentRotation);
- }
-
- /// <summary>
- /// Updates <see cref="Transform"/> properties, constrained by tracking type and tracking state.
- /// </summary>
- /// <param name="newPosition">The new local position to possibly set.</param>
- /// <param name="newRotation">The new local rotation to possibly set.</param>
- protected virtual void SetLocalTransform(Vector3 newPosition, Quaternion newRotation)
- {
- var positionValid = m_IgnoreTrackingState || (m_CurrentTrackingState & TrackingStates.Position) != 0;
- var rotationValid = m_IgnoreTrackingState || (m_CurrentTrackingState & TrackingStates.Rotation) != 0;
-
- #if HAS_SET_LOCAL_POSITION_AND_ROTATION
- if (m_TrackingType == TrackingType.RotationAndPosition && rotationValid && positionValid)
- {
- transform.SetLocalPositionAndRotation(newPosition, newRotation);
- return;
- }
- #endif
-
- if (rotationValid &&
- (m_TrackingType == TrackingType.RotationAndPosition ||
- m_TrackingType == TrackingType.RotationOnly))
- {
- transform.localRotation = newRotation;
- }
-
- if (positionValid &&
- (m_TrackingType == TrackingType.RotationAndPosition ||
- m_TrackingType == TrackingType.PositionOnly))
- {
- transform.localPosition = newPosition;
- }
- }
-
- bool HasStereoCamera(out Camera cameraComponent)
- {
- return TryGetComponent(out cameraComponent) && cameraComponent.stereoEnabled;
- }
-
- #region DEPRECATED
-
- // Disable warnings that these fields are never assigned to. They are set during Unity deserialization and migrated.
- // ReSharper disable UnassignedField.Local
- #pragma warning disable 0649
- [Obsolete]
- [SerializeField, HideInInspector]
- InputAction m_PositionAction;
- /// <summary>
- /// (Deprecated) The action to read the position value of a tracked device.
- /// Must support reading a value of type <see cref="Vector3"/>.
- /// </summary>
- /// <seealso cref="positionInput"/>
- public InputAction positionAction
- {
- get => m_PositionInput.action;
- set => positionInput = new InputActionProperty(value);
- }
-
- [Obsolete]
- [SerializeField, HideInInspector]
- InputAction m_RotationAction;
- /// <summary>
- /// (Deprecated) The action to read the rotation value of a tracked device.
- /// Must support reading a value of type <see cref="Quaternion"/>.
- /// </summary>
- /// <seealso cref="rotationInput"/>
- public InputAction rotationAction
- {
- get => m_RotationInput.action;
- set => rotationInput = new InputActionProperty(value);
- }
- #pragma warning restore 0649
- // ReSharper restore UnassignedField.Local
-
- /// <inheritdoc />
- void ISerializationCallbackReceiver.OnBeforeSerialize()
- {
- }
-
- /// <inheritdoc />
- void ISerializationCallbackReceiver.OnAfterDeserialize()
- {
- #pragma warning disable 0612 // Type or member is obsolete -- Deprecated fields are migrated to new properties.
- #pragma warning disable UNT0029 // Pattern matching with null on Unity objects -- Using true null is intentional, not operator== evaluation.
- // We're checking for true null here since we don't want to migrate if the new field is already being used, even if the reference is missing.
- // Migrate the old fields to the new properties added in Input System 1.1.0-pre.6.
- if (m_PositionInput.serializedReference is null && m_PositionInput.serializedAction is null && !(m_PositionAction is null))
- m_PositionInput = new InputActionProperty(m_PositionAction);
-
- if (m_RotationInput.serializedReference is null && m_RotationInput.serializedAction is null && !(m_RotationAction is null))
- m_RotationInput = new InputActionProperty(m_RotationAction);
- #pragma warning restore UNT0029
- #pragma warning restore 0612
- }
-
- #endregion
- }
- }
|