reference:
https://github.com/ViveIsAwesome/OpenVROverlayTest/blob/master/OpenVROverlayTest/Program.cs
https://gist.github.com/naveedmurtuza/6600103
dependency:
https://github.com/ValveSoftware/openvr/blob/master/headers/openvr_api.cs
https://github.com/ValveSoftware/openvr/tree/master/bin/win64
Here is the code to create an OpenVR overlay by C# and display text in the overlay
//Program.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Valve.VR;
namespace CSOpenVROverlayTest
{
class Program
{
public static CVRSystem hmd { get; private set; }
public static CVRCompositor compositor { get; private set; }
public static CVROverlay overlay { get; private set; }
private static int stringIndex = 0;
//ref: https://github.com/ViveIsAwesome/OpenVROverlayTest/blob/master/OpenVROverlayTest/Program.cs
static void Main(string[] args)
{
string ResourcePath = new FileInfo(Assembly.GetEntryAssembly().Location).Directory.FullName + "/Resources/";
// init
var error = EVRInitError.None;
OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
if (error != EVRInitError.None) throw new Exception();
OpenVR.GetGenericInterface(OpenVR.IVRCompositor_Version, ref error);
if (error != EVRInitError.None) throw new Exception();
OpenVR.GetGenericInterface(OpenVR.IVROverlay_Version, ref error);
if (error != EVRInitError.None) throw new Exception();
hmd = OpenVR.System;
compositor = OpenVR.Compositor;
//compositor.FadeToColor(0.5f, 0, 0, 0, 1, true);
//compositor.FadeToColor(0.5f, 0, 0, 0, 1, false);
overlay = OpenVR.Overlay;
ulong overlayHandle = 0;
EVROverlayError eVROverlayError;
eVROverlayError = overlay.CreateOverlay("overlayTestKey", "overlayTest", ref overlayHandle);
overlay.ShowOverlay(overlayHandle);
Font font = new Font(FontFamily.GenericSansSerif,
12.0F, FontStyle.Bold);
Bitmap textImage = null;
IntPtr? unmanagedPointer = null;
DrawTextToOverlay(overlayHandle, displayText, font, Color.Red, 1000, ref textImage, ref unmanagedPointer);
overlay.SetOverlayWidthInMeters(overlayHandle, 2.5f);
int count = 0;
while (true)
{
Console.Clear();
TrackedDevicePose_t[] trackedDevicePose_T = new TrackedDevicePose_t[1];
hmd.GetDeviceToAbsoluteTrackingPose(ETrackingUniverseOrigin.TrackingUniverseStanding, 0.0f, trackedDevicePose_T);
HmdMatrix34_t hmdMatrix34_T = trackedDevicePose_T[0].mDeviceToAbsoluteTracking;
PrintMatrix("hmdMatrix34_T", hmdMatrix34_T);
Console.WriteLine("");
HmdVector3_t hmdPos = new HmdVector3_t();
OpenVRMathHelper.Set(ref hmdPos, hmdMatrix34_T.Element(0, 3), hmdMatrix34_T.Element(1, 3), hmdMatrix34_T.Element(2, 3));
HmdVector3_t yVec = new HmdVector3_t();
//make overlay tranform's up vector as (0, 1, 0)
OpenVRMathHelper.Set(ref yVec, 0.0f, 1.0f, 0.0f);
//overlay tranform's forward vector (only retain the x and z components to make the overlay upright)
HmdVector3_t zVec = new HmdVector3_t();
OpenVRMathHelper.Set(ref zVec, hmdMatrix34_T.Element(0, 2), 0.0f, hmdMatrix34_T.Element(2, 2));
OpenVRMathHelper.Normalize(ref zVec);
//calculate overlay transform's right vector
HmdVector3_t xVec = OpenVRMathHelper.crossHmdVector3t(yVec, zVec);
OpenVRMathHelper.Normalize(ref xVec);
HmdVector3_t pos = new HmdVector3_t();
OpenVRMathHelper.Set(ref pos, ref hmdPos);
HmdVector3_t forward = new HmdVector3_t();
OpenVRMathHelper.Set(ref forward, ref zVec);
OpenVRMathHelper.Scale(ref forward, -5.0f);
//move overlay along hmd's forward vector from hmd
OpenVRMathHelper.Add(ref pos, ref forward);
//PrintVec("hmdPos", hmdPos);
//PrintVec("xVec", xVec);
//PrintVec("yVec", yVec);
//PrintVec("zVec", zVec);
//PrintVec("forward", forward);
//PrintVec("pos", pos);
//Console.WriteLine("");
HmdMatrix34_t overlayTransform = new HmdMatrix34_t();
OpenVRMathHelper.Set(ref overlayTransform, ref xVec, ref yVec, ref zVec, ref pos);
PrintMatrix("overlayTransform", overlayTransform);
//hmdMatrix34_T.m11 -= 5.0f;
Console.WriteLine("");
//overlay.SetOverlayTransformAbsolute(overlayHandle, ETrackingUniverseOrigin.TrackingUniverseStanding, ref hmdMatrix34_T);
overlay.SetOverlayTransformAbsolute(overlayHandle, ETrackingUniverseOrigin.TrackingUniverseStanding, ref overlayTransform);
//Console.Write(hmdMatrix34_T.m0 + ", " + hmdMatrix34_T.m1 + ", " + hmdMatrix34_T.m2 + ", " + hmdMatrix34_T.m3);Console.WriteLine();
//Console.Write(hmdMatrix34_T.m4 + ", " + hmdMatrix34_T.m5 + ", " + hmdMatrix34_T.m6 + ", " + hmdMatrix34_T.m7);Console.WriteLine();
//Console.Write(hmdMatrix34_T.m8 + ", " + hmdMatrix34_T.m9 + ", " + hmdMatrix34_T.m10 + ", " + hmdMatrix34_T.m11);Console.WriteLine();
//Console.WriteLine();
Thread.Sleep(10);
count += 10;
if(count == 1000)//after 1 sec
{
count = 0;
DrawTextToOverlay(overlayHandle, displayText, font, Color.Red, 1000, ref textImage, ref unmanagedPointer);
}
}
}
public static void PrintMatrix(string name, HmdMatrix34_t matrix)
{
Console.WriteLine("matrix: " + name);
for(int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
Console.Write(matrix.Element(i, j) + ", ");
}
Console.WriteLine("");
}
}
public static void PrintVec(string name, HmdVector3_t vec)
{
Console.WriteLine(name + ": " + vec.v0 + ", " + vec.v1 + ", " + vec.v2);
}
public static string displayText
{
get
{
string result = "";
switch(stringIndex)
{
case 0:
result = "Hi ";
break;
case 1:
result = "HiHi ";
break;
case 2:
result = "HiHiHi ";
break;
case 3:
result = "HiHiHiHi ";
break;
case 4:
result = "HiHiHiHiHi";
break;
}
stringIndex = (stringIndex + 1) % 5;
return result;
}
}
public static void DrawTextToOverlay(ulong overlayHandle, string text, Font font, Color color, int maxWidth, ref Bitmap bitmap, ref IntPtr? intPtr)
{
GC.Collect();
if (bitmap != null) { bitmap.Dispose(); }
int w, h;
DrawTextToImage(ref bitmap, text, font, color, maxWidth, out w, out h);
IntPtr? prevPtr = intPtr;
byte[] imgBytes = null;
bitmapToByteArray(bitmap, ref imgBytes);
intPtr = Marshal.AllocHGlobal(imgBytes.Length);
Marshal.Copy(imgBytes, 0, intPtr.Value, imgBytes.Length);
overlay.SetOverlayRaw(overlayHandle, intPtr.Value, (uint)w, (uint)h, 4);
if (prevPtr.HasValue)
{
Marshal.FreeHGlobal(prevPtr.Value);
}
}
//ref: https://gist.github.com/naveedmurtuza/6600103
public static Bitmap DrawTextToImage(ref Bitmap img, String text, Font font, Color textColor, int maxWidth, out int width, out int height)
{
//first, create a dummy bitmap just to get a graphics object
img = new Bitmap(1, 1);
Graphics drawing = Graphics.FromImage(img);
//measure the string to see how big the image needs to be
string textToMeasure = text.Replace(" ", "_");
SizeF textSize = drawing.MeasureString(textToMeasure, font, maxWidth);
//set the stringformat flags to rtl
StringFormat sf = new StringFormat();
//uncomment the next line for right to left languages
//sf.FormatFlags = StringFormatFlags.DirectionRightToLeft;
sf.Trimming = StringTrimming.Word;
//free up the dummy image and old graphics object
img.Dispose();
drawing.Dispose();
//create a new image of the right size
img = new Bitmap((int)textSize.Width, (int)textSize.Height);
width = (int)textSize.Width;
height = (int)textSize.Height;
drawing = Graphics.FromImage(img);
//Adjust for high quality
drawing.CompositingQuality = CompositingQuality.HighQuality;
drawing.InterpolationMode = InterpolationMode.HighQualityBilinear;
drawing.PixelOffsetMode = PixelOffsetMode.HighQuality;
drawing.SmoothingMode = SmoothingMode.HighQuality;
drawing.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
//paint the background
drawing.Clear(Color.Transparent);
//create a brush for the text
Brush textBrush = new SolidBrush(textColor);
drawing.DrawString(text, font, textBrush, new RectangleF(0, 0, textSize.Width, textSize.Height), sf);
drawing.Save();
textBrush.Dispose();
drawing.Dispose();
return img;
}
public static byte[] bitmapToByteArray(Bitmap bitmap, ref byte[] byteArray)
{
byteArray = new byte[bitmap.Width * bitmap.Height * 4];
int width = bitmap.Width;
int height = bitmap.Height;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
Color c = bitmap.GetPixel(j, i);
//byteArray[(i * width + j) * 4] = c.A;
//byteArray[(i * width + j) * 4 + 1] = c.R;
//byteArray[(i * width + j) * 4 + 2] = c.G;
//byteArray[(i * width + j) * 4 + 3] = c.B;
byteArray[(i * width + j) * 4] = c.R;
byteArray[(i * width + j) * 4 + 1] = c.G;
byteArray[(i * width + j) * 4 + 2] = c.B;
byteArray[(i * width + j) * 4 + 3] = c.A;
//Console.WriteLine(c.A);
//Console.WriteLine(c.R);
//Console.WriteLine(c.G);
//Console.WriteLine(c.B);
//Console.WriteLine("--");
}
}
return byteArray;
}
}
}
//OpenVRMathHelper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Valve.VR;
public static class OpenVRMathHelper
{
public static float calcHmdVector3tLength(ref HmdVector3_t v)
{
return (float)Math.Sqrt(v.v0 * v.v0 + v.v1 * v.v1 + v.v2 * v.v2);
}
public static void Set(ref HmdVector3_t vec, float x, float y, float z)
{
vec.v0 = x;
vec.v1 = y;
vec.v2 = z;
}
public static void Normalize(ref HmdVector3_t vec)
{
float length = calcHmdVector3tLength(ref vec);
vec.v0 = vec.v0 / length;
vec.v1 = vec.v1 / length;
vec.v2 = vec.v2 / length;
}
public static void Scale(ref HmdVector3_t vec, float scale)
{
vec.v0 = vec.v0 * scale;
vec.v1 = vec.v1 * scale;
vec.v2 = vec.v2 * scale;
}
public static void Add(ref HmdVector3_t vec, ref HmdVector3_t vec2)
{
vec.v0 = vec.v0 + vec2.v0;
vec.v1 = vec.v1 + vec2.v1;
vec.v2 = vec.v2 + vec2.v2;
}
public static void Set(ref HmdMatrix34_t matrix, ref HmdVector3_t col0, ref HmdVector3_t col1, ref HmdVector3_t col2, ref HmdVector3_t col3)
{
SetElement(ref matrix, 0, 0, col0.v0);
SetElement(ref matrix, 1, 0, col0.v1);
SetElement(ref matrix, 2, 0, col0.v2);
SetElement(ref matrix, 0, 1, col1.v0);
SetElement(ref matrix, 1, 1, col1.v1);
SetElement(ref matrix, 2, 1, col1.v2);
SetElement(ref matrix, 0, 2, col2.v0);
SetElement(ref matrix, 1, 2, col2.v1);
SetElement(ref matrix, 2, 2, col2.v2);
SetElement(ref matrix, 0, 3, col3.v0);
SetElement(ref matrix, 1, 3, col3.v1);
SetElement(ref matrix, 2, 3, col3.v2);
}
public static void SetElement(ref HmdMatrix34_t matrix, int r, int c, float value)
{
switch (r)
{
case 0:
switch (c)
{
case 0:
matrix.m0 = value;
return;
case 1:
matrix.m1 = value;
return;
case 2:
matrix.m2 = value;
return;
case 3:
matrix.m3 = value;
return;
}
break;
case 1:
switch (c)
{
case 0:
matrix.m4 = value;
return;
case 1:
matrix.m5 = value;
return;
case 2:
matrix.m6 = value;
return;
case 3:
matrix.m7 = value;
return;
}
break;
case 2:
switch (c)
{
case 0:
matrix.m8 = value;
return;
case 1:
matrix.m9 = value;
return;
case 2:
matrix.m10 = value;
return;
case 3:
matrix.m11 = value;
return;
}
break;
}
}
}