kurohuku
Posted on June 16, 2024
Let’s create a setting screen on the SteamVR dashboard. It switches which hand to display the watch overlay.
Create dashboard overlay
Dashboard overlay is an overlay that is displayed on the SteamVR dashboard. We use this overlay as the setting screen.
Create new script
Create DashboardOverlay.cs inside Scripts folder and copy the following code.
using UnityEngine;
using Valve.VR;
using System;
public class DashboardOverlay : MonoBehaviour
{
private void Start()
{
}
}
Put the script into the scene
On hierarchy, right click > Create Empty to create a new game object named DashboardOverlay
. Drag DashboardOverlay.cs
to the object.
Prepare overlay handles
Dashboard overlay consists of the main overlay and thumbnail overlay. Both overlays have their handle.
The thumbnail overlay is a small overlay at the bottom of the dashboard used to switch between overlays.
The red rectangle is the thumbnail overlay. The large "Right Hand" button is on the main overlay.
Create two variables for the two overlay handles in the DashboardOverlay.cs
.
using UnityEngine;
using Valve.VR;
using System;
public class DashboardOverlay : MonoBehaviour
{
+ private ulong dashboardHandle = OpenVR.k_ulOverlayHandleInvalid;
+ private ulong thumbnailHandle = OpenVR.k_ulOverlayHandleInvalid;
}
Create dashboard overlay
Create dashboard overlay with CreateDashboardOverlay(). (read the wiki for details)
Here, we set key
as "WatchDashboardKey"
and name
as "Watch Setting"
.
CreateDashboardOverlay()
creates two overlays and sets their handles to the variables of the 3rd and 4th arguments.
Create utility class
We want to run the program but we must initialize the OpenVR before using the API. The initialization code is in WatchOverlay.cs
. If DashboardOverlay.cs
runs earlier than that initialization, it will result in an error.
There are various ways but this time, we will create a utility to share common code like initializing OpenVR that is called from other classes.
Create new script
Create OpenVRUtil.cs
inside Scripts
folder. Copy the following code.
using UnityEngine;
using Valve.VR;
using System;
namespace OpenVRUtil
{
public static class System
{
}
}
Move OpenVR initialization
Move the InitOpenVR()
from WatchOverlay.cs
to OpenVRUtil.cs
. Add static
to allow access from other external classes.
WatchOverlay.cs
private void OnDestroy()
{
DestroyOverlay(overlayHandle);
ShutdownOpenVR();
}
- private void InitOpenVR()
- {
- if (OpenVR.System != null) return;
-
- var error = EVRInitError.None;
- OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
- if (error != EVRInitError.None)
- {
- throw new Exception("Failed to initialize OpenVR: " + error);
- }
- }
private void ShutdownOpenVR()
{
if (OpenVR.System != null)
{
OpenVR.Shutdown();
}
}
...
OpenVRUtil.cs
using UnityEngine;
using Valve.VR;
using System;
namespace OpenVRUtil
{
public static class System
{
+ // Add as public static method
+ public static void InitOpenVR()
+ {
+ if (OpenVR.System != null) return;
+
+ var error = EVRInitError.None;
+ OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
+ if (error != EVRInitError.None)
+ {
+ throw new Exception("Failed to initialize OpenVR: " + error);
+ }
+ }
}
}
Move OpenVR cleanup
Similarly, move the ShutdownOpenVR()
as a static
method.
WatchOverlay.cs
// ...
private void OnDestroy()
{
DestroyOverlay(overlayHandle);
ShutdownOpenVR();
}
- private void ShutdownOpenVR()
- {
- if (OpenVR.System != null)
- {
- OpenVR.Shutdown();
- }
- }
private ulong CreateOverlay(string key, string name)
{
var handle = OpenVR.k_ulOverlayHandleInvalid;
var error = OpenVR.Overlay.CreateOverlay(key, name, ref handle);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to dispose OpenVR: " + error);
}
return handle;
}
// ...
OpenVRUtil.cs
using UnityEngine;
using Valve.VR;
using System;
namespace OpenVRUtil
{
public static class System
{
public static void InitOpenVR()
{
if (OpenVR.System != null) return;
var initError = EVRInitError.None;
OpenVR.Init(ref initError, EVRApplicationType.VRApplication_Overlay);
if (initError != EVRInitError.None)
{
throw new Exception("Failed to initialize OpenVR: " + initError);
}
}
+ public static void ShutdownOpenVR()
+ {
+ if (OpenVR.System != null)
+ {
+ OpenVR.Shutdown();
+ }
+ }
}
}
Move overlay methods
Move overlay methods from WatchOverlay.cs
to OpenVRUtil.cs
because they will be used by other classes later.
Move all overlay methods from CreateOverlay()
to SetOverlayRenderTexture()
in WatchOverlay.cs
.
- CreateOverlay()
- DestroyOverlya()
- SetOverlayFromFile()
- ShowOverlay()
- SetoverlaySize()
- SetOverlayTransformAbsolute()
- SetOverlayTransformRelative()
- FlipOverlayVertical()
- SetOverlayRenderTexture()
WatchOverlay.cs
private void OnDestroy()
{
DestroyOverlay(overlayHandle);
OpenVRUtil.System.ShutdownOpenVR();
}
- private ulong CreateOverlay(string key, string name)
- {
- var handle = OpenVR.k_ulOverlayHandleInvalid;
- var error = OpenVR.Overlay.CreateOverlay(key, name, ref handle);
- if (error != EVROverlayError.None)
- {
- throw new Exception("Failed to create overlay: " + error);
- }
-
- return handle;
- }
-
- ...
-
- private void SetOverlayRenderTexture(RenderTexture renderTexture)
- {
- var nativeTexturePtr = renderTexture.GetNativeTexturePtr();
- var texture = new Texture_t
- {
- eColorSpace = EColorSpace.Auto,
- eType = ETextureType.DirectX,
- handle = nativeTexturePtr
- };
- var error = OpenVR.Overlay.SetOverlayTexture(overlayHandle, ref texture);
- if (error != EVROverlayError.None)
- {
- throw new Exception("Failed to draw texture: " + error);
- }
- }
Create new static class Overlay
to OpenVRUtil.cs
and add all the methods as public static
method.
OpenVRUtil.cs
namespace OpenVRUtil
{
public static class System
{
public static void InitOpenVR()
{
if (OpenVR.System != null) return;
var initError = EVRInitError.None;
OpenVR.Init(ref initError, EVRApplicationType.VRApplication_Overlay);
if (initError != EVRInitError.None)
{
throw new Exception("Failed to initialize OpenVR: " + initError);
}
}
public static void ShutdownOpenVR()
{
if (OpenVR.System != null)
{
OpenVR.Shutdown();
}
}
}
+ public static class Overlay
+ {
+ public static ulong CreateOverlay(string key, string name)
+ {
+ var handle = OpenVR.k_ulOverlayHandleInvalid;
+ var error = OpenVR.Overlay.CreateOverlay(key, name, ref handle);
+ if (error != EVROverlayError.None)
+ {
+ throw new Exception("Failed to create overlay: " + error);
+ }
+
+ return handle;
+ }
+
+ ...
+
+ public static void SetOverlayRenderTexture(ulong handle, RenderTexture renderTexture)
+ {
+ var nativeTexturePtr = renderTexture.GetNativeTexturePtr();
+ var texture = new Texture_t
+ {
+ eColorSpace = EColorSpace.Auto,
+ eType = ETextureType.DirectX,
+ handle = nativeTexturePtr
+ };
+ var error = OpenVR.Overlay.SetOverlayTexture(handle, ref texture);
+ if (error != EVROverlayError.None)
+ {
+ throw new Exception("Failed to draw texture: " + error);
+ }
+ }
+ }
}
Update method calls in existing code
Change WatchOverlay.cs
code to call the overlay methods from the OpenVRUtil
instead of WatchOverlay.cs
itself.
WatchOverlay.cs
using System;
using UnityEngine;
using Valve.VR;
+ using OpenVRUtil;
public class WatchOverlay : MonoBehaviour
{
public Camera camera;
public RenderTexture renderTexture;
private ulong overlayHandle = OpenVR.k_ulOverlayHandleInvalid;
[Range(0, 0.5f)] public float size;
[Range(-0.5f, 0.5f)] public float x;
[Range(-0.5f, 0.5f)] public float y;
[Range(-0.5f, 0.5f)] public float z;
[Range(0, 360)] public int rotationX;
[Range(0, 360)] public int rotationY;
[Range(0, 360)] public int rotationZ;
private void Start()
{
- InitOpenVR();
+ OpenVRUtil.System.InitOpenVR();
- overlayHandle = CreateOverlay("WatchOverlayKey", "WatchOverlay");
+ overlayHandle = Overlay.CreateOverlay("WatchOverlayKey", "WatchOverlay");
- FlipOverlayVertical(overlayHandle);
- SetOverlaySize(overlayHandle, size);
- ShowOverlay(overlayHandle);
+ Overlay.FlipOverlayVertical(overlayHandle);
+ Overlay.SetOverlaySize(overlayHandle, size);
+ Overlay.ShowOverlay(overlayHandle);
}
private void Update()
{
var leftControllerIndex = OpenVR.System.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand);
if (leftControllerIndex != OpenVR.k_unTrackedDeviceIndexInvalid)
{
var position = new Vector3(x, y, z);
var rotation = Quaternion.Euler(rotationX, rotationY, rotationZ);
- SetOverlayTransformRelative(overlayHandle, leftControllerIndex, position, rotation);
+ Overlay.SetOverlayTransformRelative(overlayHandle, leftControllerIndex, position, rotation);
}
- SetOverlayRenderTexture(overlayHandle, renderTexture);
+ Overlay.SetOverlayRenderTexture(overlayHandle, renderTexture);
}
private void OnApplicationQuit()
{
- DestroyOverlay(overlayHandle);
+ Overlay.DestroyOverlay(overlayHandle);
}
private void OnDestroy()
{
- ShutdownOpenVR();
+ OpenVRUtil.System.ShutdownOpenVR();
}
}
Add OpenVR initialize and cleanup
We made the utility class to share the common code. Let’s go back to the DashboardOverlay.cs
to create a dashboard overlay.
Add OpenVR initialization and cleanup code. We already call the initialize and cleanup functions in the WatchOverlay.cs
but there is no problem because if the OpenVR is already initialized or cleaned up, the methods do nothing.
DashboardOverlay.cs
using UnityEngine;
using Valve.VR;
using System;
+ using OpenVRUtil;
public class DashboardOverlay : MonoBehaviour
{
private ulong dashboardHandle = OpenVR.k_ulOverlayHandleInvalid;
private ulong thumbnailHandle = OpenVR.k_ulOverlayHandleInvalid;
private void Start()
{
+ OpenVRUtil.System.InitOpenVR();
var error = OpenVR.Overlay.CreateDashboardOverlay("WatchDashboardKey", "Watch Setting", ref dashboardHandle, ref thumbnailHandle);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to create dashboard overlay: " + error);
}
}
+ private void OnDestroy()
+ {
+ OpenVRUtil.System.ShutdownOpenVR();
+ }
}
Destroy dashboard overlay
Destroy the dashboard overlay at the end of the application.
public class DashboardOverlay : MonoBehaviour
{
private ulong dashboardHandle = OpenVR.k_ulOverlayHandleInvalid;
private ulong thumbnailHandle = OpenVR.k_ulOverlayHandleInvalid;
private void Start()
{
OpenVRUtil.System.InitOpenVR();
var error = OpenVR.Overlay.CreateDashboardOverlay("WatchDashboardKey", "Watch Setting", ref dashboardHandle, ref thumbnailHandle);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to create dashboard overlay: " + error);
}
}
+ private void OnApplicationQuit()
+ {
+ Overlay.DestroyOverlay(dashboardHandle);
+ }
private void OnDestroy()
{
OpenVRUtil.System.ShutdownOpenVR();
}
}
As a note, we can destroy the main overlay only. If we pass the thumbnail overlay handle to DestroyOverlay()
, ThumbnailCantBeDestroyed
error will occur.
Show thumbnail
Let’s show image to the thumbnail.
We made a function SetOverlayFromFile()
and a image in part.2 so we will use it.
private void Start()
{
OpenVRUtil.System.InitOpenVR();
var error = OpenVR.Overlay.CreateDashboardOverlay("WatchDashboardKey", "Watch Setting", ref dashboardHandle, ref thumbnailHandle);
if (error != EVROverlayError.None)
{
throw new Exception("ダッシュボードオーバーレイの作成に失敗しました: " + error);
}
+ var filePath = Application.streamingAssetsPath + "/sns-icon.jpg";
+ Overlay.SetOverlayFromFile(thumbnailHandle, filePath);
}
Run the program, and check if the thumbnail is shown at the bottom of the dashboard.
Destroy the dashboard overlay at the end of the application.
public class DashboardOverlay : MonoBehaviour
{
private ulong dashboardHandle = OpenVR.k_ulOverlayHandleInvalid;
private ulong thumbnailHandle = OpenVR.k_ulOverlayHandleInvalid;
private void Start()
{
OpenVRUtil.System.InitOpenVR();
var error = OpenVR.Overlay.CreateDashboardOverlay("WatchDashboardKey", "Watch Setting", ref dashboardHandle, ref thumbnailHandle);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to create dashboard overlay: " + error);
}
}
+ private void OnApplicationQuit()
+ {
+ Overlay.DestroyOverlay(dashboardHandle);
+ }
private void OnDestroy()
{
OpenVRUtil.System.ShutdownOpenVR();
}
}
As a note, we can destroy the main overlay only. If we pass the thumbnail overlay handle to DestroyOverlay()
, ThumbnailCantBeDestroyed
error will occur.
Show thumbnail
Let's show image to the thumbnail.
We made a function SetOverlayFromFile()
and a image in part.2 so we will use it.
private void Start()
{
OpenVRUtil.System.InitOpenVR();
var error = OpenVR.Overlay.CreateDashboardOverlay("WatchDashboardKey", "Watch Setting", ref dashboardHandle, ref thumbnailHandle);
if (error != EVROverlayError.None)
{
throw new Exception("ダッシュボードオーバーレイの作成に失敗しました: " + error);
}
+ var filePath = Application.streamingAssetsPath + "/sns-icon.jpg";
+ Overlay.SetOverlayFromFile(thumbnailHandle, filePath);
}
Run the program, and check if the thumbnail is shown at the bottom of the dashboard.
We don’t draw any image yet to the main overlay so it shows nothing when the thumbnail is clicked.
The name
passed to CreateDashboardOverlay()
is shown when the laser pointer hovers over the thumbnail. This time, it’s "Watch Settings"
.
Create setting screen
Let’s create the setting screen.
First, divide the game objects into two groups: the watch group and the dashboard group.
Watch Group
Right click in hierarchy > Create Empty to create an empty game object named Watch.
Move existing WatchOverlay
, Camera
, Canvas
objects under the Watch
object.
Dashboard group
Create the below objects under the Dashboard
object.
- Camera
- UI > Canvas
Camera setting
Select Camera under the Dashboard
, set Clear Flags
to Solid Color in the inspector.
Click Background
color, then change to opaque gray (RGBA = 64, 64, 64, 255).
Also, remove
the AudioListener component.
Create render texture
Create render texture for dashboard overlay.
In the project window, right click Assets/RenderTexture folder > Render Texture to create a new render texture asset.
Change the asset name to DashboardRenderTexture
.
Set the Size to 1024 x 768 in the inspector.
Click Camera
under Dashboard
in the hierarchy, drag DashboardRenderTexture
asset to Target Texture
property.
Here, the dashboard camera output goes to the render texture asset.
Canvas setting
Select Canvas
object under Dashboard
.
Set Render
Mode to Screen Space — Camera
in the Canvas
inspector.
Drag Camera
object under Dashboard
to Render Camera
.
Set Plane Distance
to 10
.
Move group
Open Dashboard
the object’s inspector.
Set Position X to 20 to avoid overlapping groups.
Create button
Right click Canvas
object under Dashboard
then create UI > Button — TextMeshPro to create a new button named LeftHandButton
.
Click Text (TMP)
under LeftHandButton
, change the text to “Left Hand”.
Select LeftHandButton
and set the Width to 700 and Height to 200.
Select the Text (TMP)
and set the Font Size to 100.
Duplicate LeftHandButton
by right click > Duplicate.
Change the duplicated object name to RightHandButton
.
Similarly, change the button text to “Right Hand”.
Set LeftHandButton Pos Y to 150 and RightHandButton Pos Y to -150.
Here, the appearance of the setting screen is done.
Draw to dashboard
Display the setting screen as a dashboard overlay.
Add camera variable
Add camera
member variable to DashboardOverlay.cs
.
public class DashboardOverlay : MonoBehaviour
{
+ public Camera camera;
private ulong dashboardHandle = OpenVR.k_ulOverlayHandleInvalid;
private ulong thumbnailHandle = OpenVR.k_ulOverlayHandleInvalid;
// ...
Open DashboardOverlay
object inspector, drag Camera
object under Dashboard
to the Camera
variable.
Add render texture
Add renderTexture
variable in DashboardOverlay.cs
.
public class DashboardOverlay : MonoBehaviour
{
public Camera camera;
+ public RenderTexture renderTexture;
private ulong dashboardHandle = OpenVR.k_ulOverlayHandleInvalid;
private ulong thumbnailHandle = OpenVR.k_ulOverlayHandleInvalid;
// ...
Select DashboardOverlay
in the hierarchy, drag Assets/RenderTextures/DashboardRenderTexture
asset to the Render Texture
variable.
Flip vertical and set size
In advance, flip the texture vertically with FlipOverlayVertical()
and set overlay size to 2.5 m with SetOverlaySize()
.
Draw render texture to dashboard overlay
Create Update()
and draw the render texture to the dashboard overlay.
DashboardOverlay.cs
public class DashboardOverlay : MonoBehaviour
{
public Camera camera;
public RenderTexture renderTexture;
private ulong dashboardHandle = OpenVR.k_ulOverlayHandleInvalid;
private ulong thumbnailHandle = OpenVR.k_ulOverlayHandleInvalid;
private void Start()
{
OpenVRUtil.System.InitOpenVR();
var error = OpenVR.Overlay.CreateDashboardOverlay("WatchDashboardKey", "Watch Setting", ref dashboardHandle, ref thumbnailHandle);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to create dashboard overlay: " + error);
}
var filePath = Application.streamingAssetsPath + "/sns-icon.jpg";
Overlay.SetOverlayFromFile(thumbnailHandle, filePath);
renderTexture = new RenderTexture(1024, 768, 16, RenderTextureFormat.ARGBFloat);
camera.targetTexture = renderTexture;
Overlay.SetOverlaySize(dashboardHandle, 2.5f);
Overlay.FlipOverlayVertical(dashboardHandle);
}
+ private void Update()
+ {
+ Overlay.SetOverlayRenderTexture(dashboardHandle, renderTexture);
+ }
// ...
Run the program, open the dashboard, and click the thumbnail. The setting screen should be shown.
Organize code
Move the creating dashboard overlay code to CreateDashboardOverlay()
.
It has to return two values dashboardhandle
and thumbnailHandle
, so we use a tuple to combine them.
Add the method to the utility’s Overlay
class.
OpenVRUtil.cs
...
public static ulong CreateOverlay(string key, string name)
{
var handle = OpenVR.k_ulOverlayHandleInvalid;
var error = OpenVR.Overlay.CreateOverlay(key, name, ref handle);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to create overlay: " + error);
}
return handle;
}
+ public static (ulong, ulong) CreateDashboardOverlay(string key, string name)
+ {
+ ulong dashboardHandle = 0;
+ ulong thumbnailHandle = 0;
+ var error = OpenVR.Overlay.CreateDashboardOverlay(key, name, ref dashboardHandle, ref thumbnailHandle);
+ if (error != EVROverlayError.None)
+ {
+ throw new Exception("Failed to create dashboard overlay: " + error);
+ }
+
+ return (dashboardHandle, thumbnailHandle);
+ }
...
Accordingly, change the function call.
DashboardOverlay.cs
private void Start()
{
OpenVRUtil.System.InitOpenVR();
- var error = OpenVR.Overlay.CreateDashboardOverlay("WatchDashboardKey", "Watch Setting", ref dashboardHandle, ref thumbnailHandle);
- if (error != EVROverlayError.None)
- {
- throw new Exception("Failed to create dashboard overlay: " + error);
- }
+ (dashboardHandle, thumbnailHandle) = Overlay.CreateDashboardOverlay("WatchDashboardKey", "Watch Setting");
var filePath = Application.streamingAssetsPath + "/sns-icon.jpg";
Overlay.SetOverlayFromFile(thumbnailHandle, filePath);
Overlay.SetOverlaySize(dashboardHandle, 2.5f);
Overlay.FlipOverlayVertical(dashboardHandle);
}
Final code
DashboardOverlay.cs
using UnityEngine;
using Valve.VR;
using OpenVRUtil;
public class DashboardOverlay : MonoBehaviour
{
public Camera camera;
public RenderTexture renderTexture;
private ulong dashboardHandle = OpenVR.k_ulOverlayHandleInvalid;
private ulong thumbnailHandle = OpenVR.k_ulOverlayHandleInvalid;
private void Start()
{
OpenVRUtil.System.InitOpenVR();
(dashboardHandle, thumbnailHandle) = Overlay.CreateDashboardOverlay("WatchDashboardKey", "Watch Setting");
var filePath = Application.streamingAssetsPath + "/sns-icon.jpg";
Overlay.SetOverlayFromFile(thumbnailHandle, filePath);
Overlay.FlipOverlayVertical(dashboardHandle);
Overlay.SetOverlaySize(dashboardHandle, 2.5f);
}
private void Update()
{
Overlay.SetOverlayRenderTexture(dashboardHandle, renderTexture);
}
private void OnApplicationQuit()
{
Overlay.DestroyOverlay(dashboardHandle);
}
private void OnDestroy()
{
OpenVRUtil.System.ShutdownOpenVR();
}
}
OpenVRUtil.cs
using UnityEngine;
using Valve.VR;
using System;
namespace OpenVRUtil
{
public static class System
{
public static void InitOpenVR()
{
if (OpenVR.System != null) return;
var error = EVRInitError.None;
OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
if (error != EVRInitError.None)
{
throw new Exception("Failed to initialize OpenVR: " + error);
}
}
public static void ShutdownOpenVR()
{
if (OpenVR.System != null)
{
OpenVR.Shutdown();
}
}
}
public static class Overlay
{
public static ulong CreateOverlay(string key, string name)
{
var handle = OpenVR.k_ulOverlayHandleInvalid;
var error = OpenVR.Overlay.CreateOverlay(key, name, ref handle);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to create overlay: " + error);
}
return handle;
}
public static (ulong, ulong) CreateDashboardOverlay(string key, string name)
{
ulong dashboardHandle = 0;
ulong thumbnailHandle = 0;
var error = OpenVR.Overlay.CreateDashboardOverlay(key, name, ref dashboardHandle, ref thumbnailHandle);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to create dashboard overlay: " + error);
}
return (dashboardHandle, thumbnailHandle);
}
public static void DestroyOverlay(ulong handle)
{
if (handle != OpenVR.k_ulOverlayHandleInvalid)
{
var error = OpenVR.Overlay.DestroyOverlay(handle);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to dispose overlay: " + error);
}
}
}
public static void SetOverlayFromFile(ulong handle, string path)
{
var error = OpenVR.Overlay.SetOverlayFromFile(handle, path);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to draw image file: " + error);
}
}
public static void ShowOverlay(ulong handle)
{
var error = OpenVR.Overlay.ShowOverlay(handle);
if (error != EVROverlayError.None)
{
throw new Exception("Failed show overlay: " + error);
}
}
public static void SetOverlaySize(ulong handle, float size)
{
var error = OpenVR.Overlay.SetOverlayWidthInMeters(handle, size);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to set overlay size: " + error);
}
}
public static void SetOverlayTransformAbsolute(ulong handle, Vector3 position, Quaternion rotation)
{
var rigidTransform = new SteamVR_Utils.RigidTransform(position, rotation);
var matrix = rigidTransform.ToHmdMatrix34();
var error = OpenVR.Overlay.SetOverlayTransformAbsolute(handle, ETrackingUniverseOrigin.TrackingUniverseStanding, ref matrix);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to set overlay position: " + error);
}
}
public static void SetOverlayTransformRelative(ulong handle, uint deviceIndex, Vector3 position, Quaternion rotation)
{
var rigidTransform = new SteamVR_Utils.RigidTransform(position, rotation);
var matrix = rigidTransform.ToHmdMatrix34();
var error = OpenVR.Overlay.SetOverlayTransformTrackedDeviceRelative(handle, deviceIndex, ref matrix);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to set overlay position: " + error);
}
}
public static void FlipOverlayVertical(ulong handle)
{
var bounds = new VRTextureBounds_t
{
uMin = 0,
uMax = 1,
vMin = 1,
vMax = 0
};
var error = OpenVR.Overlay.SetOverlayTextureBounds(handle, ref bounds);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to flip texture: " + error);
}
}
public static void SetOverlayRenderTexture(ulong handle, RenderTexture renderTexture)
{
if (!renderTexture.IsCreated()) return;
var nativeTexturePtr = renderTexture.GetNativeTexturePtr();
var texture = new Texture_t
{
eColorSpace = EColorSpace.Auto,
eType = ETextureType.DirectX,
handle = nativeTexturePtr
};
var error = OpenVR.Overlay.SetOverlayTexture(handle, ref texture);
if (error != EVROverlayError.None)
{
throw new Exception("Failed to draw texture: " + error);
}
}
}
}
Now, we have created the setting screen as a dashboard overlay. We will make the button events in the next part.
Posted on June 16, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.