VR Drawing in Unity with the HTC Vive

Made with

  • Unity LTS version 2019.4.11
  • OpenVR package
  • SteamVR asset
  • HTC Vive.

Result:

Video recording of me testing it out.

Assignment

Our assignment was to develop a tilt-brush type experience in Unity.

  • So I created an empty gameObject with the controller handler on it. You can make this gameObject a child of your controller in the XR rig. ControllerHandler.cs on the bottom of the page.
  • I have a couple of Cube Game objects also around the scene, each one of them has the Color Selector script attached to them. ColorSelector.cs on the bottom of the page.
  • And Last, I have a “Brush” prefab, which consists of a Cube with the Brush Handler script attached. I put this brush prefab NEAR the controller object I made earlier in step 1. BrushHandler.cs on the bottom of the page.

While I get Githtub LFS to work properly, you can find the copies of my scripts at the bottom.

  • I had to add the OpenVR package from the package manager.
  • Then went to Project Settings > Player > XR Settings and clicked on the Virtual Reality Supported checkbox.
Player XR Settings for OpenVR to work.
  • Then, I installed the SteamVR asset from the asset store in order to get some prefabs for the Vive Controllers and premade event handlers.
SteamVR Asset. Highly recommended.

After importing

I added the player prefab which came with the SteamVR interaction Demoscene, as well as the Teleport Prefab. Each of them contained the necessary scripting for the HMD + controllers setup and the control-based teleportation, respectively.

The Teleport prefab was interesting. It contained a TeleportArea prefab which had a script with the same name. This object created the active area or plane in which we could teleport on. By default, this included a ‘Locked’ grid with several active hotspots. This meant you can only teleport to the hotspot areas.

This was easy to remove, by deselecting the ‘Locked’ tick box in the TeleportArea script. I then went into the script and disabled the default material.

I got to play.

First I made a ‘UI’ on my left controller, which consisted of little cubes floating around each of a different color. When you touch a cube with the brush in your right hand, your brush changes to that color.

In order to do that, I made a new action in the SteamVR default Action set. The defaults already comes with several actions, such as Teleport, Squeeze, and others. You can find that in Window > SteamVR Input

Creating custom Actions

I created a custom action called DisplayWristColorPicker and DrawBrush, as seen in the image above. Since they would be assigned to the trigger button, with a click setting, I set them as boolean. I would need to set these actions to my controllers in the SteamVR binding UI.

Open binding UI lets you set the custom actions to the hardware, in this case, the Vive controllers.

Assigning the actions to controller inputs.

Then, I went to the trigger of each controller and added a ‘click’ action with my Display Wrist Color Picker.

Creating the click trigger.

These bindings can be seen in JSON format by clicking on the Show Developer Output button.

I then made a script called ViveInput to check if the actions were being registered as I played….and they did!

To check whether the trigger on the controller is being pressed or not, I used the following function:

public void Update()
    {
        //Check if we are pressing the trigger and invoking the Draw Brush Action
        if (SteamVR_Actions.default_DrawBrush.GetState(SteamVR_Input_Sources.Any))
        {
            print("Displaying Brush!");
            
            Draw();
        }

    }

Scripts!

BrushHandler.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BrushHandler : MonoBehaviour {

    //create a private variable that stores the currentColor

    private Color currentColor;

    //create a public variable that returns the prefab for drawing (hint, prefabs are gameobjects)

    public GameObject brushShape;

    //create a public function that sets up the color of the currentColor (hint, function must have a Color variable)
    public void setBrushColor(Color setColor)
    {
        //Set our material's color whatever color the Color selector is. We invoke this function from the colorSelector.
        transform.GetComponent<MeshRenderer>().material.color = setColor;
    }

    //create a public function that returns the Color of the currentColor
    public Color returnCurrentBrushColor()
    {
        // Returns the applied color when invoked
        return brushShape.transform.GetComponent<MeshRenderer>().material.color;
    }

}

ControllerHandler.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Valve.VR;

public class ControllerHandler : MonoBehaviour {
    private BrushHandler brushHandler;

    public void Update()
    {
        //Check if we are pressing the trigger and invoking the Draw Brush Action
        if (SteamVR_Actions.default_DrawBrush.GetState(SteamVR_Input_Sources.Any))
        {
            print("Painting with Brush!");
            // Debug.Log(transform.position);
            Draw();
        }

    }
    public void Draw()
    {

        //find the brush controller using findobject of type

        brushHandler = (BrushHandler)FindObjectOfType(typeof(BrushHandler));

        //see which color is currently applied in the brush
        Color brushHandlerColor = brushHandler.returnCurrentBrushColor();

        //update the material of your brush to that color
        brushHandler.GetComponent<MeshRenderer>().material.color = brushHandlerColor;

        //draw our brush here using instantiating

        Instantiate(brushHandler, transform.position, transform.rotation);

    
    }
}

ColorSelector.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ColorSelector : MonoBehaviour {

    //Create a public Color Type that can be set from the inspector to be used in each menu Item

    public Color colorSelector;
    private void Start()
    {
        //Set our material color to the colorSelector color

        transform.GetComponent<MeshRenderer>().material.color = colorSelector;
    }

    //Hint Look at Color class in Unity SDK

    //Setup Collider Trigger to send the color of this object to the Brush Handler

    private void OnTriggerEnter(Collider other)
    {
        // Find the brush in scene
        BrushHandler brushHandler = (BrushHandler)FindObjectOfType(typeof(BrushHandler));

        // If there is a brush, set our brush's color with the setBrushColor Method
        if (brushHandler)
        {

                other.gameObject.GetComponent<BrushHandler>().setBrushColor(colorSelector);
        }
        
    }

}