Search Unity

#CodeSnippets: Toggle Vive’s front facing camera and “Tron Mode” at run time

, junho 16, 2017

How to programmatically turn the front facing camera video and camera for chaperone bounds on and off on the HTC Vive.

Unity’s 2017 hack week happened a couple of weeks ago in Nyborg, Denmark. As our contribution, my team decided to explore physical VR and UX/UI with HTC Vive trackers. In our proposed scenario, the application starts with a step that provides quick and easy calibration to scan the physical world into the virtual. This is followed by a fully immersive VR experience where virtual objects and surfaces are aligned with physical ones.

The HTC Vive headset has a camera that users can activate in SteamVR to show video of the actual room they are in, and also to activate what is commonly referred to as “Tron Mode,” a setting called Allow Camera for Chaperone Bounds which detects and highlights the edges of objects. For the proposed scenario, we wanted to test and evaluate three different options for allowing users to “draw” the physical world:

  1. Render a quad next to one of the controllers with video of the room, just like in SteamVR dashboard.
  2. Enable Tron Mode
  3. Options (1) and (2) at the same time.

We needed to programmatically go through each of these options, turning the front facing camera video and the Allow Camera for Chaperone Bounds setting on and off. In this blog post we share the code for how this was accomplished for the hack week project.

Rendering video to a quad next to the controller was easy since the SteamVR Unity plugin provides a sample scene that does it: SteamVR_TestTrackedCamera located in the folder SteamVR -> Extras has all we need. After parenting the TrackedCamera game object to one of the controllers in the object hierarchy, toggling it on and off is just a matter of enabling/disabling the associated game object.

Toggling Tron Mode is done by calling SetBool in OpenVR settings:

We then define an enumeration for the video mode, and call the toggle functions when a menu button is pressed. The following behavior is assigned to TrackedCamera:

The video below shows what activating each video mode at run time looks like in our hack week project. By combining the construction tool programmed by my colleague Josh Naylor and the video mode switching behavior, I am able to build a box in VR that is aligned with a real, physical one. After building the virtual box, I disable video and become completely immersed in a virtual reality scene that is calibrated with the physical space.

The code snippet in this article is a solution in the context of a hack week project. For scalability and modularity, developers would probably want to handle video modes independently, and add additional features like allowing resizing of the video quad. While in this experience we found that just the video quad next to the controller was enough to outline small-to-medium-size objects, UX professionals would probably find it valuable to experiment and evaluate if Tron Mode is more suitable for certain scenarios, or whether combining both video feed and Tron makes a difference in terms of immersion and alignment. I share this code as a starting point and reference.

For a full video of our project, check the video on YouTube. My teammates were: Greg Madison, Jonas Minnberg, Josh Naylor and Tristan Genevet.

What other use cases do you have for toggling these features at run time? We’d love to hear them!

Comentários encerrados.

  1. Terrence A Young

    junho 19, 2017 às 12:57 am

    Quite amazing!

  2. using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    public class ColliderCheck : MonoBehaviour {
    bool Checking;

    void Start () {
    Checking = false;

    }
    void OnTriggerEnter(Collider coll)
    {
    if (coll.transform.tag == “Spare”) {
    Debug.Log (“enter”);
    gameObject.GetComponent ().material.color = new Color32 (255, 0, 0, 255);
    StartCoroutine (WaitForAgainColorChange ());
    }

    }

    IEnumerator WaitForAgainColorChange()
    {
    yield return new WaitForSeconds (3f);
    gameObject.GetComponent ().material.color = new Color32 (255, 255, 255, 255);

    yield return new WaitForSeconds (1f);
    DeletChild.instance.DeletChildAfterSomeTime ();
    yield return new WaitForSeconds (3f);
    ChildDetect.instance.FunctionCallFromOtherScript ();

    }

    }

  3. using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    public class ChildDetect : MonoBehaviour {
    public GameObject GridPrefab;
    // public GameObject[] Childs;
    // public GameObject[] Parents;
    public GameObject Clone;
    public List RuntimeList;
    public List CloneList;
    public static ChildDetect instance;
    public int getvalue;
    public Vector3 targetScale;
    int Num;

    // Use this for initialization
    void Awake () {
    instance = this;
    Num = 10;

    Clone = Instantiate (GridPrefab);

    for(int i=0;i<Clone.transform.childCount;i++)
    {
    RuntimeList.Add (Clone.transform.GetChild(i).gameObject);

    }

    }
    void Start()
    {
    StartCoroutine (ScaleObject ());
    for (int i = 0; i < RuntimeList.Count; i++) {
    CloneList.Add (RuntimeList [i].transform.GetChild (0).gameObject);
    }

    }

    IEnumerator ScaleObject()
    {

    float scaleDuration = 5;
    Vector3 targetScale = new Vector3 (1.5f,1.5f,1.5f);
    getvalue =Random.Range (0, 8);
    if (getvalue != Num) {
    Num = getvalue;
    Vector3 actualScale = RuntimeList [getvalue].transform.GetChild (0).transform.localScale;
    Debug.Log (getvalue+"getvalue");
    for (float t = 0; t < 1; t += Time.deltaTime / scaleDuration) {

    RuntimeList [getvalue].transform.GetChild (0).transform.localScale = Vector3.Lerp (actualScale, targetScale, t);

    yield return null;
    }

    StartCoroutine (Waitforboxcollider ());

    } else {
    Debug.Log ("enter else");
    StartCoroutine (ScaleObject ());
    }

    }

    public void FunctionCallFromOtherScript()
    {
    StartCoroutine (ScaleObject());
    }
    IEnumerator Waitforboxcollider()
    {
    yield return new WaitForSeconds (1f);
    if (RuntimeList [getvalue].transform.GetChild (0).transform.localScale == targetScale) {

    RuntimeList [getvalue].transform.GetChild (0).GetComponent ().enabled = true;
    }
    }
    }

  4. using UnityEngine;
    using System.Collections;

    public class DeletChild : MonoBehaviour {
    public static DeletChild instance;
    // Use this for initialization
    void Start () {
    instance = this;
    }

    // Update is called once per frame
    void Update () {

    }

    public void DeletChildAfterSomeTime()
    {
    Debug.Log (gameObject.name);
    Destroy (ChildDetect.instance.RuntimeList[ChildDetect.instance.getvalue].transform.GetChild(0).gameObject);
    }
    }

  5. The embedded video (for tron mode) just shows the hack week video (same as linked below)

    1. Dioselin Gonzalez

      junho 16, 2017 às 7:05 pm

      Thanks for catching that! The article has been updated with the correct video.