/// OSVR-Unity Connection /// /// http://sensics.com/osvr /// /// /// Copyright 2014 Sensics, Inc. /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// using UnityEngine; using System.Collections; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System; namespace OSVR { namespace Unity { //*This class is a wrapper for the OSVR-Unity Rendering Plugin osvrUnityRenderingPlugin.dll, // which brings in functionality from the OSVR RenderManager project. RenderManager features inculde: // - DirectMode -- compatible with nVidia cards with a driver that has been modified to white-list the display that you are using. // - TimeWarp // - Distortion Correction // // osvrUnityRenderingPlugin.dll, osvrRenderManager.dll, SDL2.dll, and glew32.dll must be in the Plugins/x86 or x64 folders. // Requires Unity 5.2+ //*/ public class OsvrRenderManager : MonoBehaviour { [StructLayout(LayoutKind.Sequential)] private struct OSVR_ProjectionMatrix { public double left; public double right; public double top; public double bottom; public double nearClip; //< Cannot name "near" because Visual Studio keyword public double farClip; } [StructLayout(LayoutKind.Sequential)] private struct OSVR_ViewportDescription { public double left; //< Left side of the viewport in pixels public double lower; //< First pixel in the viewport at the bottom. public double width; //< Last pixel in the viewport at the top public double height; //< Last pixel on the right of the viewport in pixels } public const int RENDER_EVENT = 0; public const int SHUTDOWN_EVENT = 1; public const int UPDATE_RENDERINFO_EVENT = 2; private const string PluginName = "osvrUnityRenderingPlugin"; [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void DebugLog(string log); private static readonly DebugLog debugLog = DebugWrapper; private static readonly IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(debugLog); private static void DebugWrapper(string log) { Debug.Log(log); } //Create and Register RenderBuffers [DllImport(PluginName)] private static extern Byte ConstructRenderBuffers(); //Create a RenderManager object in the plugin, passing in a ClientContext [DllImport(PluginName)] private static extern Byte CreateRenderManagerFromUnity(OSVR.ClientKit.SafeClientContextHandle /*OSVR_ClientContext*/ ctx); [DllImport(PluginName)] private static extern OSVR.ClientKit.Pose3 GetEyePose(int eye); [DllImport(PluginName)] private static extern OSVR_ProjectionMatrix GetProjectionMatrix(int eye); //get the render event function that we'll call every frame via GL.IssuePluginEvent [DllImport(PluginName)] private static extern IntPtr GetRenderEventFunc(); [DllImport(PluginName)] private static extern OSVR_ViewportDescription GetViewport(int eye); // Allow for calling into the debug console from C++ [DllImport(PluginName)] private static extern void LinkDebug([MarshalAs(UnmanagedType.FunctionPtr)]IntPtr debugCal); // OnRenderEvent is not needed // Pass a pointer to a texture (RenderTexture.GetNativeTexturePtr()) to the plugin // @todo native code may change the return type to OSVR_ReturnCode. // If so, change the return type here to Byte [DllImport(PluginName)] private static extern int SetColorBufferFromUnity(System.IntPtr texturePtr, int eye); [DllImport(PluginName)] private static extern void SetFarClipDistance(double farClipPlaneDistance); [DllImport(PluginName)] private static extern void SetIPD(double ipdMeters); [DllImport(PluginName)] private static extern void SetNearClipDistance(double nearClipPlaneDistance); [DllImport(PluginName)] private static extern void ShutdownRenderManager(); // UnityPluginLoad is not needed // UnityPluginUnload is not needed private bool _linkDebug = false; //causes crash on exit if true, only enable for debugging //persistent singleton private static OsvrRenderManager _instance; /// /// Use to access the single instance of this object/script in your game. /// /// The instance, or null in case of error public static OsvrRenderManager instance { get { if (_instance == null) { _instance = GameObject.FindObjectOfType(); if (_instance == null) { Debug.LogError("[OSVR-Unity] RenderManager not found."); } else { //DontDestroyOnLoad(_instance.gameObject); } } return _instance; } } void Awake() { //if an instance of this singleton does not exist, set the instance to this object and make it persist if (_instance == null) { _instance = this; //DontDestroyOnLoad(this); } else { //if an instance of this singleton already exists, destroy this one if (_instance != this) { Destroy(this.gameObject); } } } void OnDisable() { ExitRenderManager(); } //Initialize use of RenderManager via CreateRenderManager call public int InitRenderManager() { if (_linkDebug) { //this will cause a crash when exiting the Unity editor or an application //only use for debugging purposes, do not leave on for release. LinkDebug(functionPointer); // Hook our c++ plugin into Unity's console log. } return CreateRenderManager(ClientKit.instance.context); } //Create and Register RenderBuffers in RenderManager //Called after RM is created and after Unity RenderTexture's are created and assigned via SetEyeColorBuffer public int ConstructBuffers() { return ConstructRenderBuffers(); } public void SetNearClippingPlaneDistance(float near) { SetNearClipDistance((double)near); } public void SetFarClippingPlaneDistance(float far) { SetFarClipDistance((double)far); } public void SetIPDMeters(float ipd) { SetIPD((double)ipd); } //"Recenter" based on current head orientation public void SetRoomRotationUsingHead() { #if UNITY_5_2 || UNITY_5_3 || UNITY_5_4 || UNITY_5_5 ClientKit.instance.context.SetRoomRotationUsingHead(); GL.IssuePluginEvent(GetRenderEventFunc(), 3); #endif } //Clear the room-to-world transform, undo a call to SetRoomRotationUsingHead public void ClearRoomToWorldTransform() { #if UNITY_5_2 || UNITY_5_3 || UNITY_5_4 || UNITY_5_5 ClientKit.instance.context.ClearRoomToWorldTransform(); GL.IssuePluginEvent(GetRenderEventFunc(), 4); #endif } //Get the pose of a given eye from RenderManager public OSVR.ClientKit.Pose3 GetRenderManagerEyePose(int eye) { return GetEyePose(eye); } //Get the viewport of a given eye from RenderManager public OSVR.ClientKit.Viewport GetEyeViewport(int eye) { OSVR.ClientKit.Viewport v = new OSVR.ClientKit.Viewport(); OSVR_ViewportDescription viewportDescription = GetViewport(eye); v.Left = (int)viewportDescription.left; v.Bottom = (int)viewportDescription.lower; v.Width = (int)viewportDescription.width; v.Height = (int)viewportDescription.height; return v; } //Get the projection matrix of a given eye from RenderManager public Matrix4x4 GetEyeProjectionMatrix(int eye) { OSVR_ProjectionMatrix pm = GetProjectionMatrix(eye); return PerspectiveOffCenter((float)pm.left, (float)pm.right, (float)pm.bottom, (float)pm.top, (float)pm.nearClip, (float)pm.farClip); } //Returns a Unity Matrix4x4 from the provided boundaries //from http://docs.unity3d.com/ScriptReference/Camera-projectionMatrix.html static Matrix4x4 PerspectiveOffCenter(float left, float right, float bottom, float top, float near, float far) { float x = 2.0F * near / (right - left); float y = 2.0F * near / (top - bottom); float a = (right + left) / (right - left); float b = (top + bottom) / (top - bottom); float c = -(far + near) / (far - near); float d = -(2.0F * far * near) / (far - near); float e = -1.0F; Matrix4x4 m = new Matrix4x4(); m[0, 0] = x; m[0, 1] = 0; m[0, 2] = a; m[0, 3] = 0; m[1, 0] = 0; m[1, 1] = y; m[1, 2] = b; m[1, 3] = 0; m[2, 0] = 0; m[2, 1] = 0; m[2, 2] = c; m[2, 3] = d; m[3, 0] = 0; m[3, 1] = 0; m[3, 2] = e; m[3, 3] = 0; return m; } //Call the Unity Rendering Plugin to initialize the RenderManager public int CreateRenderManager(OSVR.ClientKit.ClientContext clientContext) { int result; try { result = CreateRenderManagerFromUnity(clientContext.ContextHandle); } catch (DllNotFoundException e) { result = -1; Debug.LogError("[OSVR-Unity] Could not load " + e.Message + "\nosvrUnityRenderingPlugin.dll, or one of its dependencies, is missing from the project " + "or architecture doesn't match.\n"); } return result; } //Pass pointer to eye-camera RenderTexture to the Unity Rendering Plugin public void SetEyeColorBuffer(IntPtr colorBuffer, int eye) { SetColorBufferFromUnity(colorBuffer, eye); } //Get a pointer to the plugin's rendering function public IntPtr GetRenderEventFunction() { return GetRenderEventFunc(); } //Shutdown RenderManager and Dispose of the ClientContext we created for it public void ExitRenderManager() { ShutdownRenderManager(); } //helper functions to determine is RenderManager is supported //Is the RenderManager supported? Requires D3D11 or OpenGL, currently. public bool IsRenderManagerSupported() { bool support = true; #if UNITY_ANDROID Debug.Log("[OSVR-Unity] RenderManager not yet supported on Android."); support = false; #endif if (!SystemInfo.graphicsDeviceVersion.Contains("OpenGL") && !SystemInfo.graphicsDeviceVersion.Contains("Direct3D 11")) { Debug.LogError("[OSVR-Unity] RenderManager not supported on " + SystemInfo.graphicsDeviceVersion + ". Only Direct3D11 is currently supported."); support = false; } if (!SystemInfo.supportsRenderTextures) { Debug.LogError("[OSVR-Unity] RenderManager not supported. RenderTexture (Unity Pro feature) is unavailable."); support = false; } if (!IsUnityVersionSupported()) { Debug.LogError("[OSVR-Unity] RenderManager not supported. Unity 5.2+ is required for RenderManager support."); support = false; } return support; } //Unity 5.2+ is required as the plugin uses the native plugin interface introduced in Unity 5.2 public bool IsUnityVersionSupported() { bool support = true; try { string version = new Regex(@"(\d+\.\d+)\..*").Replace(Application.unityVersion, "$1"); if (new Version(version) < new Version("5.2")) { support = false; } } catch { Debug.LogWarning("[OSVR-Unity] Unable to determine Unity version from: " + Application.unityVersion); support = false; } return support; } } } }