There are two questions being asked in this thread.
1) How do you allow a system-level recenter with Unity’s OpenXR plugin?
The short answer: Use “Floor” for the tracking origin in Unity and then set OpenXRSettings.SetAllowRecentering to true. (Actually, it seems this is the default for “Floor” now, and you should set SetAllowRecentering to false if you don’t want a system-level recenter, except the current PC OpenXR runtime don’t currently let you prevent system-level recentering…)
The long answer:
According to the OpenXR 1.0 spec (which is what everything is currently using), there are reference spaces for tracking that are relevant to this conversation: LOCAL, STAGE, and optionally LOCAL FLOOR.
-
LOCAL allows system-level recentering, but it is centered on the head. It is intended for “seated scale” games.
-
STAGE is centered on the boundary rectangle and does not allow system-level recentering. It is intended for “room scale” games.
-
LOCAL FLOOR, provided by an optional extension, is centered on the floor under the player and allows a system-level recenter. It is intended for “standing scale” games.
Now, for the confusing part. In Unity’s XR API, you can set the tracking origin to “device” or “floor”.
-
When using Unity’s OpenXR plugin, “device” uses the LOCAL OpenXR space (allows system recentering, but is centered on the head)
-
“Floor” used to use the STAGE OpenXR space (does not allow recentering, always centered on the boundary), but I think now defaults to LOCAL FLOOR (allows recentering, centered on floor under user), if it’s available.
-
The 1.9 release of Unity’s OpenXR plugin added OpenXRSettings.SetAllowRecentering, which if set to true will make “Floor” use the OpenXR LOCAL FLOOR space if the OpenXR runtime being used on the user’s system supports it, and if set to false will use STAGE. If the OpenXR runtime being used on the user’s system does not support LOCAL FLOOR, Unity will try to emulate it presumably by watching for system recenter events to the LOCAL space and then applying offsets to the LOCAL or STAGE space.
So which OpenXR runtimes support LOCAL FLOOR? Here’s the even more confusing part…
-
The Meta Quest OpenXR runtime correctly implements LOCAL, STAGE, and the optional LOCAL FLOOR space, as defined by the OpenXR spec. It seems Meta authored the LOCAL FLOOR extension so they could use it on Quest, so this makes sense.
-
Both Valve’s SteamVR OpenXR runtime and Meta’s Rift/Link/PC OpenXR runtime incorrectly allow system-level recentering for the STAGE space, so it works like LOCAL FLOOR even though it should not.
-
Valve has recently added support for the LOCAL FLOOR space to SteamVR’s OpenXR runtime. I convinced them to make it so STAGE is not recenterable and to add LOCAL FLOOR for that use case instead, which they did, but then they reverted the change to the STAGE space recentering a couple of weeks later because some games were incorrectly relying on STAGE being able to be recentered.
-
Meta’s Rift/Link/PC OpenXR runtime does not support LOCAL FLOOR, even though Meta authored the extension (but as mentioned above, their STAGE space incorrectly works like LOCAL FLOOR).
Hopefully the OpenXR 1.1 spec, which now has LOCAL FLOOR as part of the core spec instead of an extension, will unify the correct behavior across all runtimes so that LOCAL FLOOR is the space standing games should use if they want recentering and STAGE is the space they should use if they don’t. It would also be great if Unity added a third option to their XR API to differentiate between these two spaces instead of grouping them both under “Floor” and requiring the call to SetAllowRecentering to choose between the two.
2) How do you force a recenter with Unity’s OpenXR plugin?
You just do it yourself. Give your tracking origin game object a parent. When you want to recenter, offset the tracking origin game object inside its parent so that the player is now positioned where you want them to be. Use OpenXRFeature.OnAppSpaceChange to watch for system recenters, and when that happens, clear your manual offset.
I’m kind of surprised Unity doesn’t expose their own recenter function that does this behind-the-scenes, since it seems like they’re already doing something like it when they emulate the LOCAL FLOOR space on platforms where it is not available.