Skip to content

FIX: Trackedposedriver stops tracking (ISXB-1555) #2189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions Assets/Tests/InputSystem/Plugins/XRTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ENABLE_VR is not defined on Game Core but the assembly is available with limited features when the XR module is enabled.
#if ENABLE_VR || UNITY_GAMECORE
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using NUnit.Framework;
Expand All @@ -11,6 +12,7 @@
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.InputSystem.XR;
using UnityEngine.TestTools;
using UnityEngine.XR;

using Usages = UnityEngine.InputSystem.CommonUsages;
Expand Down Expand Up @@ -629,6 +631,54 @@ public void Components_TrackedPoseDriver_DoesNotEnableOrDisableReferenceActions(
Assert.That(trackingStateInput.action.enabled, Is.False);
}

[UnityTest]
[Category("Components")]
public IEnumerator CanUseTrackedPoseDriverWithoutTrackingAction()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this test.

{
var go = new GameObject();
var tpd = go.AddComponent<TrackedPoseDriver>();
tpd.updateType = TrackedPoseDriver.UpdateType.UpdateAndBeforeRender;
tpd.trackingType = TrackedPoseDriver.TrackingType.RotationAndPosition;
tpd.ignoreTrackingState = false;
var transform = tpd.transform;

var positionAction = new InputAction(binding: "<XRController>/devicePosition");
var rotationAction = new InputAction(binding: "<XRController>/deviceRotation");
tpd.positionInput = new InputActionProperty(positionAction);
tpd.rotationInput = new InputActionProperty(rotationAction);

yield return null;

Assert.That(positionAction.controls.Count, Is.EqualTo(0));
Assert.That(rotationAction.controls.Count, Is.EqualTo(0));

var device = InputSystem.AddDevice<XRController>();
InputSystem.AddDeviceUsage(device, "RightHand");

yield return null;

Assert.That(positionAction.controls.Count, Is.EqualTo(1));
Assert.That(rotationAction.controls.Count, Is.EqualTo(1));

var position = new Vector3(1f, 2f, 3f);
var rotation = new Quaternion(0.09853293f, 0.09853293f, 0.09853293f, 0.9853293f);
using (StateEvent.From(device, out var stateEvent))
{
device.devicePosition.WriteValueIntoEvent(position, stateEvent);
device.deviceRotation.WriteValueIntoEvent(rotation, stateEvent);

transform.position = Vector3.zero;
transform.rotation = Quaternion.identity;
InputSystem.QueueEvent(stateEvent);
InputSystem.Update(InputUpdateType.Dynamic);
}

yield return null;

Assert.That(transform.position, Is.EqualTo(position));
Assert.That(transform.rotation, Is.EqualTo(rotation));
}

[Test]
[Category("Components")]
public void Components_TrackedPoseDriver_RequiresResolvedTrackingStateBindings()
Expand Down
1 change: 1 addition & 0 deletions Packages/com.unity.inputsystem/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ however, it has to be formatted properly to pass verification tests.
- Fixed PlayerInput component automatically switching away from the default ActionMap set to 'None'.
- Fixed a console error being shown when targeting visionOS builds in 2022.3.
- Fixed a Tap Interaction issue with analog controls. The Tap interaction would keep re-starting after timeout. [ISXB-627](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-627)
- Fixed TrackedPoseDriver stops updating position and rotation when device is added after its initialization. [ISXB-1555](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1555)
- Fixed PlayerInput component not working with C# Wrappers (ISXB-1535). This reverted changes done to fix [ISXB-920](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-920) but users can now fix it themselves.

## [1.14.0] - 2025-03-20
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@
protected void OnEnable()
{
InputSystem.onAfterUpdate += UpdateCallback;
InputSystem.onDeviceChange += OnDeviceChanged;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only thing I am thinking around this one if it truly captures what would drive an action resolve (or might cause unnecessary checking)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come (before my regression of this) it worked then?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tracking was never set to None, but always kept at Position & Rotation when no tracking action was assigned. Like that there was no need to reevaluate the tracking state.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There will be some amount of unnecessary checking for sure, if any (not trackable) device is connected/disconnected for instance. But I am wondering if that's so critical, it probably depends on how frequently deviceChange events happen.

Copy link
Collaborator

@ekcoh ekcoh Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a HUP I think we would be fine with this and see it as an optimization if it could be reduced in the future.

BindActions();

// Read current input values when becoming enabled,
Expand All @@ -446,6 +447,7 @@
{
UnbindActions();
InputSystem.onAfterUpdate -= UpdateCallback;
InputSystem.onDeviceChange -= OnDeviceChanged;
}

/// <summary>
Expand Down Expand Up @@ -484,7 +486,7 @@
else
m_CurrentRotation = transform.localRotation;

ReadTrackingState(hasResolvedPositionInputControl, hasResolvedRotationInputControl);
ReadTrackingState();

m_IsFirstUpdate = false;
}
Expand All @@ -495,7 +497,40 @@
OnUpdate();
}

void ReadTrackingState(bool hasResolvedPositionInputControl, bool hasResolvedRotationInputControl)
void OnDeviceChanged(InputDevice inputDevice, InputDeviceChange inputDeviceChange)
{
if (m_IsFirstUpdate)
return;
ReadTrackingStateWithoutTrackingAction();
}

/// <summary>
/// React to changes of devices to stop the tracking of position / rotation or both if a device is removed, starts the tracking if
/// a device is added.
/// </summary>
void ReadTrackingStateWithoutTrackingAction()
{
var trackingStateAction = m_TrackingStateInput.action;
if (trackingStateAction != null && trackingStateAction.m_BindingsCount != 0)
return;

var hasResolvedPositionInputControl = HasResolvedControl(m_PositionInput.action);
var hasResolvedRotationInputControl = HasResolvedControl(m_RotationInput.action);

// 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.
// Check if we have transform and rotation controls to drive the pose.
if (hasResolvedPositionInputControl && hasResolvedRotationInputControl)
m_CurrentTrackingState = TrackingStates.Position | TrackingStates.Rotation;
else if (hasResolvedPositionInputControl)
m_CurrentTrackingState = TrackingStates.Position;

Check warning on line 526 in Packages/com.unity.inputsystem/InputSystem/Plugins/XR/TrackedPoseDriver.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Plugins/XR/TrackedPoseDriver.cs#L526

Added line #L526 was not covered by tests
else if (hasResolvedRotationInputControl)
m_CurrentTrackingState = TrackingStates.Rotation;

Check warning on line 528 in Packages/com.unity.inputsystem/InputSystem/Plugins/XR/TrackedPoseDriver.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Plugins/XR/TrackedPoseDriver.cs#L528

Added line #L528 was not covered by tests
else
m_CurrentTrackingState = TrackingStates.None;
}

void ReadTrackingState()
{
var trackingStateAction = m_TrackingStateInput.action;
if (trackingStateAction != null && !trackingStateAction.enabled)
Expand All @@ -504,29 +539,16 @@
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.
// Check if we have transform and rotation controls to drive the pose.
if (hasResolvedPositionInputControl && hasResolvedRotationInputControl)
m_CurrentTrackingState = TrackingStates.Position | TrackingStates.Rotation;
else if (hasResolvedPositionInputControl)
m_CurrentTrackingState = TrackingStates.Position;
else if (hasResolvedRotationInputControl)
m_CurrentTrackingState = TrackingStates.Rotation;
else
m_CurrentTrackingState = TrackingStates.None;
}
else if (HasResolvedControl(trackingStateAction))
if (HasResolvedControl(trackingStateAction))
{
// 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.
m_CurrentTrackingState = (TrackingStates)trackingStateAction.ReadValue<int>();
return;
}
ReadTrackingStateWithoutTrackingAction();
}

/// <summary>
Expand Down