-->

分類

2018年3月11日 星期日

[VR] create OpenVR Overlay by C#

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;
        }
    }
}