wwt-windows-client/OculusWrap/Wrap.cs

477 строки
14 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using ovrBool = System.Byte;
namespace OculusWrap
{
/// <summary>
/// Provides access to methods that are independent of an HMD, as well as a method used to create a new Hmd object.
/// </summary>
public class Wrap :IDisposable
{
/// <summary>
/// Creates a new Wrap instance.
/// </summary>
/// <param name="useUnsafeImplementation">
/// When set to true, the Oculus runtime will be loaded using the unsafe implementation.
///
/// The unsafe implementation provides a faster execution, by bypassing the managed security checks that are normally
/// performed when transitioning from managed to unmanaged code.
///
/// Use the unsafe implementation with extreme care. Incorrect use can create security weaknesses.
/// </param>
/// <see cref="https://msdn.microsoft.com/en-us/library/62a3eyh4(v=vs.100).aspx"/>
public Wrap(bool useUnsafeImplementation=false)
{
if(useUnsafeImplementation)
{
if(Environment.Is64BitProcess)
OVR = new OVR64Unsafe();
else
OVR = new OVR32Unsafe();
}
else
{
if(Environment.Is64BitProcess)
OVR = new OVR64();
else
OVR = new OVR32();
}
}
/// <summary>
/// Cleans up unmanaged resources.
/// </summary>
~Wrap()
{
Dispose(false);
}
#region IDisposable Members
/// <summary>
/// Clean up unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose pattern implementation of dispose method.
/// </summary>
/// <param name="disposing">True if disposing, false if finalizing.</param>
protected virtual void Dispose(bool disposing)
{
if(Disposed)
return;
// Ensure that all created HMDs have been disposed.
foreach(Hmd hmd in CreatedHmds)
{
if(!hmd.Disposed)
hmd.Dispose();
}
CreatedHmds.Clear();
if(Initialized)
Shutdown();
// Deallocate unmanaged memory again.
FreeHGlobal(ref m_eyePosesPtr);
OVR.Dispose();
OVR = null;
Disposed = true;
}
/// <summary>
/// Describes if the object has been disposed.
/// </summary>
public bool Disposed
{
get;
private set;
}
#endregion
/// <summary>
/// Interface to Oculus runtime methods.
/// </summary>
private OVRBase OVR
{
get;
set;
}
#region Public methods
/// <summary>
/// Detects Oculus Runtime and Device Status
///
/// Checks for Oculus Runtime and Oculus HMD device status without loading the LibOVRRT
/// shared library. This may be called before ovr_Initialize() to help decide whether or
/// not to initialize LibOVR.
/// </summary>
/// <param name="timeoutMsec">Specifies a timeout to wait for HMD to be attached or 0 to poll.</param>
/// <returns>Returns a DetectResult object indicating the result of detection.</returns>
/// <see cref="OVRTypes.DetectResult"/>
public OVRTypes.DetectResult Detect(int timeoutMsec)
{
return OVR.Detect(timeoutMsec);
}
/// <summary>
/// Initializes all Oculus functionality.
/// </summary>
/// <param name="initializationParameters">
/// Initialization parameters to pass to the ovr_Initialize call.
/// </param>
/// <remarks>
/// Library init/shutdown, must be called around all other OVR code.
/// No other functions calls besides ovr_InitializeRenderingShim are allowed
/// before ovr_Initialize succeeds or after ovr_Shutdown.
/// </remarks>
public bool Initialize(OVRTypes.InitParams initializationParameters=null)
{
if(Initialized)
throw new InvalidOperationException("The Oculus wrapper has already been initialized.");
/*
// Ensure that the DllOvr.dll is loaded, using the bitness matching that of the current process.
LoadDllOvr();
*/
OVRTypes.Result success = OVR.Initialize(initializationParameters);
if(success < OVRTypes.Result.Success)
return false;
Initialized = true;
return true;
}
/// <summary>
/// Shuts down all Oculus functionality.
/// </summary>
public bool Shutdown()
{
if(!Initialized && !RenderingShimInitialized)
return true;
OVR.Shutdown();
/*
// Unload previously loaded DllOVr.dll.
UnloadDllOvr();
*/
Initialized = false;
RenderingShimInitialized = false;
return true;
}
/// <summary>
/// Returns version string representing libOVR version. Static, so
/// string remains valid for app lifespan
/// </summary>
/// <remarks>
/// Use Marshal.PtrToStringAnsi() to retrieve version string.
/// </remarks>
public string GetVersionString()
{
IntPtr versionPtr = OVR.GetVersionString();
string version = Marshal.PtrToStringAnsi(versionPtr);
return version;
}
/// <summary>
/// Send a message string to the system tracing mechanism if enabled (currently Event Tracing for Windows)
/// </summary>
/// <param name="level">
/// One of the ovrLogLevel constants.
/// </param>
/// <param name="message">
/// A string.
/// </param>
/// <returns>
/// Returns the length of the message, or -1 if message is too large
/// </returns>
public int TraceMessage(OVRTypes.LogLevel level, string message)
{
return OVR.TraceMessage(level, message);
}
/// <summary>
/// Creates a handle to an HMD.
///
/// Upon success the returned Hmd must be eventually freed with Dispose() when it is no longer needed.
/// </summary>
/// <param name="graphicsLuid">
/// Provides a system specific graphics adapter identifier that locates which
/// graphics adapter has the HMD attached. This must match the adapter used by the application
/// or no rendering output will be possible. This is important for stability on multi-adapter systems. An
/// application that simply chooses the default adapter will not run reliably on multi-adapter systems.
/// </param>
public Hmd Hmd_Create(out OVRTypes.GraphicsLuid graphicsLuid)
{
IntPtr hmdPtr = IntPtr.Zero;
graphicsLuid = new OVRTypes.GraphicsLuid();
OVRTypes.Result result = OVR.Create(ref hmdPtr, ref graphicsLuid);
if(result < OVRTypes.Result.Success)
return null;
Hmd hmd = new Hmd(OVR, hmdPtr);
// Ensure that this created HMD is disposed, when this Wrap class is being disposed.
CreatedHmds.Add(hmd);
return hmd;
}
/// <summary>
/// Returns last error for HMD state. Returns null for no error.
///
/// String is valid until next call or GetLastError or HMD is destroyed.
/// Pass null hmd to get global errors (during create etc).
/// </summary>
public OVRBase.ErrorInfo GetLastError()
{
OVRTypes.ErrorInfo errorInfo;
OVR.GetLastErrorInfo(out errorInfo);
return errorInfo;
}
/// <summary>
/// Used to generate projection from ovrEyeDesc::Fov.
/// </summary>
public OVRBase.Matrix4f Matrix4f_Projection(OVRBase.FovPort fov, float znear, float zfar, OVRBase.ProjectionModifier projectionModifier)
{
return OVR.Matrix4f_Projection(fov, znear, zfar, projectionModifier);
}
/// <summary>
/// Extracts the required data from the result of ovrMatrix4f_Projection.
/// </summary>
/// <param name="projection">Specifies the project matrix from which to extract ovrTimewarpProjectionDesc.</param>
/// <param name="projectionModFlags">A combination of the ProjectionModifier flags.</param>
/// <returns>Returns the extracted ovrTimewarpProjectionDesc.</returns>
/// <see cref="OVRTypes.TimewarpProjectionDesc"/>
public OVRBase.TimewarpProjectionDesc TimewarpProjectionDesc_FromProjection(OVRBase.Matrix4f projection, OVRBase.ProjectionModifier projectionModFlags)
{
return OVR.TimewarpProjectionDesc_FromProjection(projection, projectionModFlags);
}
/// <summary>
/// Used for 2D rendering, Y is down
/// orthoScale = 1.0f / pixelsPerTanAngleAtCenter
/// orthoDistance = distance from camera, such as 0.8m
/// </summary>
public OVRBase.Matrix4f Matrix4f_OrthoSubProjection(OVRBase.Matrix4f projection, OVRBase.Vector2f orthoScale, float orthoDistance, float eyeViewAdjustX)
{
return OVR.Matrix4f_OrthoSubProjection(projection, orthoScale, orthoDistance, eyeViewAdjustX);
}
/// <summary>
/// Computes offset eye poses based on headPose returned by ovrTrackingState.
/// </summary>
/// <param name="headPose">
/// Indicates the HMD position and orientation to use for the calculation.
/// </param>
/// <param name="hmdToEyeViewOffset">
/// Can be ovrEyeRenderDesc.HmdToEyeViewOffset returned from
/// ovrHmd_GetRenderDesc. For monoscopic rendering, use a vector that is the average
/// of the two vectors for both eyes.
/// </param>
/// <param name="outEyePoses">
/// If outEyePoses are used for rendering, they should be passed to
/// SubmitFrame in LayerEyeFov.RenderPose or LayerEyeFovDepth.RenderPose.
/// </param>
public void CalcEyePoses(OVRBase.Posef headPose, OVRBase.Vector3f[] hmdToEyeViewOffset, ref OVRBase.Posef[] outEyePoses)
{
if(hmdToEyeViewOffset.Length != 2)
throw new ArgumentException("The hmdToEyeViewOffset argument must contain 2 elements.", "hmdToEyeViewOffset");
if(outEyePoses.Length != 2)
throw new ArgumentException("The outEyePoses argument must contain 2 elements.", "outEyePoses");
if(m_eyePosesPtr == IntPtr.Zero)
{
// Allocate and copy managed struct to unmanaged memory.
m_poseFSize = Marshal.SizeOf(typeof(OVRBase.Posef));
m_eyePosesPtr = Marshal.AllocHGlobal(m_poseFSize*2);
}
OVR.CalcEyePoses(headPose, hmdToEyeViewOffset, m_eyePosesPtr);
outEyePoses[0] = (OVRBase.Posef) Marshal.PtrToStructure(m_eyePosesPtr, typeof(OVRBase.Posef));
outEyePoses[1] = (OVRBase.Posef) Marshal.PtrToStructure(m_eyePosesPtr+m_poseFSize, typeof(OVRBase.Posef));
}
/// <summary>
/// Returns global, absolute high-resolution time in seconds. This is the same
/// value as used in sensor messages.
/// </summary>
public double GetTimeInSeconds()
{
return OVR.GetTimeInSeconds();
}
/// <summary>
/// Tracking poses provided by the SDK come in a right-handed coordinate system. If an application
/// is passing in ovrProjection_LeftHanded into Matrix4f_Projection, then it should also use
/// this function to flip the HMD tracking poses to be left-handed.
///
/// While this utility function is intended to convert a left-handed ovrPosef into a right-handed
/// coordinate system, it will also work for converting right-handed to left-handed since the
/// flip operation is the same for both cases.
/// </summary>
/// <param name="pose">Pose that is right-handed</param>
public OVRBase.Posef Posef_FlipHandedness(OVRBase.Posef pose)
{
OVRTypes.Posef inputOutputPose = pose;
OVR.Posef_FlipHandedness(ref inputOutputPose, ref inputOutputPose);
return inputOutputPose;
}
/*
/// <summary>
/// Waits until the specified absolute time.
/// </summary>
/// <remarks>
/// This function may be removed in a future version.
/// </remarks>
public double WaitTillTime(double absTime)
{
return OVRBase.WaitTillTime(absTime);
}
*/
#endregion
#region Public properties
/// <summary>
/// Determines if InitializeRenderingShim() has been called, without calling Shutdown().
/// </summary>
public bool RenderingShimInitialized
{
get;
private set;
}
/// <summary>
/// Determines if Initialize() has been called, without calling Shutdown().
/// </summary>
public bool Initialized
{
get;
private set;
}
#endregion
#region Private helper methods
/// <summary>
/// Frees the memory at the specified pointer and sets the pointer to IntPtr.Zero.
/// </summary>
/// <param name="pointer">Pointer to memory that will be freed.</param>
/// <remarks>
/// This method is used to free memory previously allocated by a call to Marshal.AllocHGlobal(...).
/// </remarks>
private void FreeHGlobal(ref IntPtr pointer)
{
if(pointer != IntPtr.Zero)
{
Marshal.FreeHGlobal(pointer);
pointer = IntPtr.Zero;
}
}
#endregion
/*
#region Private methods
/// <summary>
/// This method is used to load the DllOVR.dll into memory, before calling any of it's DllImported methods.
///
/// This is done to allow loading an x86 version of the DllOvr.dll, for an x86 process, or an x64 version of it,
/// for an x64 process.
/// </summary>
private void LoadDllOvr()
{
if(DllOvrPtr == IntPtr.Zero)
{
// Retrieve the folder of the OculusWrap.dll.
string executingAssemblyFolder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string subfolder;
if(Environment.Is64BitProcess)
subfolder = "x64";
else
subfolder = "x86";
string filename = Path.Combine(executingAssemblyFolder, subfolder, OVR. DllOvrDll);
// Check that the DllOvrDll file exists.
bool exists = File.Exists(filename);
if(!exists)
throw new DllNotFoundException("Unable to load the file \""+filename+"\", the file wasn't found.");
DllOvrPtr = OVR.LoadLibrary(filename);
if(DllOvrPtr == IntPtr.Zero)
{
int win32Error = Marshal.GetLastWin32Error();
throw new Win32Exception(win32Error, "Unable to load the file \""+filename+"\", LoadLibrary reported error code: "+win32Error+".");
}
}
}
/// <summary>
/// Frees previously loaded DllOvr.dll, from process memory.
/// </summary>
private void UnloadDllOvr()
{
if(DllOvrPtr != IntPtr.Zero)
{
bool success = OVR.FreeLibrary(DllOvrPtr);
if(success)
DllOvrPtr = IntPtr.Zero;
}
}
#endregion
*/
#region Private properties
/// <summary>
/// Pointer to the DllOvr module, after it has been loaded, using a call to OVR.LoadLibrary.
/// </summary>
private IntPtr DllOvrPtr
{
get;
set;
}
/// <summary>
/// Set of created HMDs.
/// </summary>
/// <remarks>
/// This set is used to ensure that all created HMDs are also disposed.
/// </remarks>
private HashSet<Hmd> CreatedHmds
{
get
{
return m_createdHmds;
}
}
#endregion
#region Private fields
private HashSet<Hmd> m_createdHmds = new HashSet<Hmd>();
private IntPtr m_eyePosesPtr = IntPtr.Zero;
private int m_poseFSize = 0;
#endregion
}
}