Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Help with Oculus Quest Reorientation on LevelLoad

Discussion in 'VR' started by Jimbo_Slice, Oct 12, 2019.

  1. Jimbo_Slice

    Jimbo_Slice

    Joined:
    Oct 1, 2015
    Posts:
    44
    Hi everyone,

    I am having some difficulty with an Oculus Quest project that consists of 3 scenes, each scene has its own OVRCameraRig with positions and rotations set in the Editor.

    However, when running on the Quest if the player moves say a few meters from the centre of the tracking space and rotates a bit then the next scene loads - the player's starting position in the next scene is not positioned or facing where it is supposed to.

    I have done a lot of Googling and made some discoveries:

    - Superhot VR had to implement a game mechanic to recenter and reorient the player before loading a new level (not an option for me)
    - The default TrackingSpaceType for Quest is set to stationary - so I have set it to room scale on Awake
    - OVRManager.display.RecenterPose has been disabled by Oculus for Quest projects so calling it does nothing
    - I tried to parent all the children/anchors of OVRCameraRig>TrackingSpace under an empty GameObject and then rotate/position that but changing the hierarchy this way seems to confuse the OVRCameraRig, which re-instantiates the anchor objects all over again
    - I have also tried enabling/disabling the ResetTrackerOnLoad and ReorientHMDOnControllerRecentre options on OVRManager but that doesn't seem to do the job either

    As of yet, I have found no definitive answer for reorienting the OVRCameraRig on level load. I would like to ensure that regardless of where the player is in their tracking space that they start the scene at certain position and are rotated to face a certain direction.

    Has anyone had any luck with this?
     
    hippocoder likes this.
  2. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I'm having similar problems with having a consistent floor height on Quest. At this point I think Unity's implementation isn't tested well - noticed devs tell me they haven't tried it on Quest. Which is surprising considering how it's outselling rift 10:1

    There's https://docs.unity3d.com/ScriptReference/XR.InputTracking.Recenter.html but not sure if it's what you're looking for...
     
    BrainSlugs83, ROBYER1 and Jimbo_Slice like this.
  3. Jimbo_Slice

    Jimbo_Slice

    Joined:
    Oct 1, 2015
    Posts:
    44
    I just saw this part of the documentation and then didn't think it was worth trying it...

    "This only works with seated and standing experiences. Room scale experiences are not affected by Recenter."
     
    Sathish1020, hippocoder and ROBYER1 like this.
  4. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    My origin is reset if I hold right oculus menu button though... so it basically is working for roomscale I guess.
     
  5. Jimbo_Slice

    Jimbo_Slice

    Joined:
    Oct 1, 2015
    Posts:
    44
    I am sure there is a more efficient way to do this, but for now I have got this working for me. Just thought I would throw it up here in case anyone else is having a similar issue (and up against a deadline xD):

    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3. using UnityEngine.XR;
    4. using UnityEngine.SceneManagement;
    5.  
    6. [System.Serializable]
    7. public class SceneLoadCameraReset
    8. {
    9.     public int sceneIndex;
    10.     public Vector3 startPosition;
    11.     public float startYRotation;
    12. }
    13.  
    14. public class SetTrackingType : MonoBehaviour
    15. {
    16.     [SerializeField] SceneLoadCameraReset[] sceneLoadOptions;
    17.  
    18.     Transform _OVRCameraRig;
    19.     Transform _centreEyeAnchor;
    20.  
    21.     private void OnEnable()
    22.     {
    23.         SceneManager.sceneLoaded += ResetCameraOnSceneLoad;
    24.     }
    25.  
    26.     private void OnDisable()
    27.     {
    28.         SceneManager.sceneLoaded -= ResetCameraOnSceneLoad;
    29.     }
    30.  
    31.     private void Awake()
    32.     {
    33.         XRDevice.SetTrackingSpaceType(TrackingSpaceType.RoomScale);
    34.     }
    35.  
    36.     //Helper function to find the correct instances of OVRCameraRig and CentreEyeAnchor
    37.     void FindOVRCameraRig()
    38.     {
    39.         OVRCameraRig ovr = FindObjectOfType<OVRCameraRig>();
    40.  
    41.         if (ovr)
    42.         {
    43.             _OVRCameraRig = ovr.transform;
    44.             _centreEyeAnchor = ovr.centerEyeAnchor;
    45.         }
    46.         else
    47.         {
    48.             Debug.Log("No OVRCameraRig object found");
    49.         }
    50.     }
    51.  
    52.  
    53.     //Calls ResetCamera based on the current scene which was just loaded
    54.     void ResetCameraOnSceneLoad(Scene scene, LoadSceneMode mode)
    55.     {
    56.         FindOVRCameraRig();
    57.  
    58.         for (int i = 0; i < sceneLoadOptions.Length; i++)
    59.         {
    60.             if (scene.buildIndex == sceneLoadOptions[i].sceneIndex)
    61.             {
    62.                 StartCoroutine(ResetCamera(sceneLoadOptions[i].startPosition, sceneLoadOptions[i].startYRotation));
    63.             }
    64.         }
    65.     }
    66.  
    67.     //Resets the OVRCameraRig's position and Y-axis rotation to help align the player's starting position and view to the target parameters
    68.     IEnumerator ResetCamera(Vector3 targetPosition, float targetYRotation)
    69.     {
    70.         EditorDebugOffset();
    71.  
    72.         yield return new WaitForEndOfFrame();
    73.  
    74.         float currentRotY = _centreEyeAnchor.eulerAngles.y;
    75.         float difference = targetYRotation - currentRotY;
    76.         _OVRCameraRig.Rotate(0, difference, 0);
    77.  
    78.        Vector3 newPos = new Vector3(targetPosition.x - _centreEyeAnchor.position.x, 0, targetPosition.z - _centreEyeAnchor.position.z);
    79.         _OVRCameraRig.transform.position += newPos;
    80.     }
    81. }
    It just adjusts the OVRCameraRig's position / rotation to compensate for the centreEyeAnchor (player) being moved or looking the wrong way.
     
    Last edited: Oct 15, 2019
    radiantboy, Fenikkel, Desoxi and 2 others like this.
  6. SKNKanimation

    SKNKanimation

    Joined:
    Feb 24, 2017
    Posts:
    12

    You're my hero this morning, Jimbo. <3
     
    radiantboy and Jimbo_Slice like this.
  7. cyberscorpia_unity

    cyberscorpia_unity

    Joined:
    Dec 15, 2017
    Posts:
    3
    glenneroo and Fenikkel like this.
  8. Fenikkel

    Fenikkel

    Joined:
    Sep 24, 2019
    Posts:
    20
    OVRManager.display.RecenterPose works in TrackingOriginType -> FloorLevel.

    In the case of TrackingOriginType -> EyeLevel, just recenter the rotation (of the Y) and keeps the position in the tracking area.

    My real problem was that OVRManager.display.RecenterPose works in Oculus Link and Oculus Air Link, but not in Virtual Desktop.

    @Jimbo_Slice solution solve this problem. It's like a teleport.

    My function (I'm not using XR Management):

    Code (CSharp):
    1.        private Transform m_CameraRig;
    2.     private Transform m_CentreEyeAnchor;
    3.     public OVRCameraRig m_OVRCameraRig;
    4.  
    5.     void Start()
    6.     {
    7.    m_CentreEyeAnchor = m_OVRCameraRig.centerEyeAnchor;
    8.         m_CameraRig = m_OVRCameraRig.transform;
    9.     }
    10.  
    11.   private void ResetVRPosition(Transform teleportPoint) //Do the same as OVRManager.display.RecenterPose() but works in Virtual Desktop and EyeLevelTracking
    12.     {
    13.  
    14.         float currentRotY = m_CentreEyeAnchor.eulerAngles.y;
    15.         float targetYRotation = 0.0f;
    16.         float difference = targetYRotation - currentRotY;
    17.         m_CameraRig.Rotate(0, difference, 0);
    18.  
    19.         Vector3 newPos = new Vector3(teleportPoint.position.x - m_CentreEyeAnchor.position.x, 0, teleportPoint.position.z - m_CentreEyeAnchor.position.z);
    20.         m_CameraRig.transform.position += newPos;
    21.  
    22.     }
     
    Claytonious likes this.
  9. jeppe79

    jeppe79

    Joined:
    Sep 17, 2019
    Posts:
    74
    I found this post:
    https://stackoverflow.com/a/50422418/14381716

    According to this the headset will overwrite the main camera position and that's why we can't modify the camera position directly. There a link to the docs, but I haven't been able to locate the information in there yet.

    Since all examples I have seen so far do point to modifying a parent transform, I guess this is the way.

    I'm still having some problems implementing the above examples.
     
    Last edited: Jul 27, 2021
  10. jeppe79

    jeppe79

    Joined:
    Sep 17, 2019
    Posts:
    74
    Here's my take on resetting the position to it's original.
    Note this also resets the height (y), desirable for my use case, might not be for others.

    It simply offsets the localPosition of centerEyeAnchor and mirrors it in trackingSpace.


    Code (CSharp):
    1. public OVRCameraRig m_OVRCameraRig;
    2.  
    3.     void ResetPosition()
    4.     {
    5.         float currentRotY = m_OVRCameraRig.centerEyeAnchor.eulerAngles.y;
    6.         float targetRotY = 0.0f;
    7.         float difference = targetRotY - currentRotY;
    8.         m_OVRCameraRig.transform.Rotate(0, difference, 0);
    9.         Vector3 currentPosition = m_OVRCameraRig.centerEyeAnchor.localPosition;
    10.         m_OVRCameraRig.trackingSpace.localPosition = new Vector3(OffsetValue(currentPosition.x), OffsetValue(currentPosition.y), OffsetValue(currentPosition.z));
    11.     }
    12.  
    13.     float OffsetValue(float value)
    14.     {
    15.         if (value != 0)
    16.         {
    17.             value *= -1;
    18.         }
    19.         return value;
    20.     }
     
  11. diegoztrombone

    diegoztrombone

    Joined:
    Mar 23, 2022
    Posts:
    1
    This solution works for me, thanks you!!
     
  12. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,592

    Thanks @Jimbo_Slice ! You rock! That works, I have made a simplified version below.
    Code (csharp):
    1.  
    2. ///this works!
    3.     public void RecenterHeadset()
    4.     {
    5.         StartCoroutine(ResetCamera(new Vector3(90, 0, 0), 0));
    6.     }
    7.     //Resets the OVRCameraRig's position and Y-axis rotation to help align the player's starting position and view to the target parameters
    8.     IEnumerator ResetCamera(Vector3 targetPosition, float targetYRotation)
    9.     {    
    10.         Transform _OVRCameraRig = startupScript.ovr.transform;
    11.         Transform _centreEyeAnchor = startupScript.ovr.centerEyeAnchor;
    12.      
    13.         //EditorDebugOffset();
    14.         yield return new WaitForEndOfFrame();
    15.         float currentRotY = _centreEyeAnchor.eulerAngles.y;
    16.         float difference = targetYRotation - currentRotY;
    17.         _OVRCameraRig.Rotate(0, difference, 0);
    18.    
    19.         Vector3 newPos = new Vector3(targetPosition.x - _centreEyeAnchor.position.x, 0, targetPosition.z - _centreEyeAnchor.position.z);
    20.         _OVRCameraRig.transform.position += newPos;
    21.     }
    22.