wwt-windows-client/OculusWrap/Layers.cs

731 строка
20 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using ovrTextureSwapChain = System.IntPtr;
namespace OculusWrap
{
/// <summary>
/// Handles allocation and deallocation of the list of layer data.
///
/// This class helps reduce the number of unmanaged memory allocations,
/// by remembering which layers were allocated and only deallocating memory again,
/// when those layers change.
///
/// Having to manage a list of different types and different sizes of layers is a
/// new feature in Oculus SDK 0.6.0.0.
/// </summary>
public class Layers :IDisposable
{
/// <summary>
/// Yup, this is a finalizer. It finalizes stuff :p
/// </summary>
~Layers()
{
Dispose(false);
}
#region IDisposable Members
/// <summary>
/// Clean up the allocated HMD.
/// </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;
if(!disposing)
{
Trace.WriteLine("The Layers with hashcode "+GetHashCode()+" was not disposed and will leak unmanaged memory.");
// This class contains a list of unmanaged data, which cannot be deallocated when finalizing this class,
// because the contained list could have been finalized before this class was finalized.
return;
}
DeallocateUnmanaged();
GC.SuppressFinalize(this);
Disposed = true;
}
/// <summary>
/// Describes if the object has been disposed.
/// </summary>
public bool Disposed
{
get;
private set;
}
#endregion
#region Public methods
/// <summary>
/// Add a new LayerEyeFov layer to the end of the list of layers.
/// </summary>
/// <returns>Added layer.</returns>
public LayerEyeFov AddLayerEyeFov()
{
if(Disposed)
throw new ObjectDisposedException("Layers");
LayerEyeFov layer = new LayerEyeFov();
m_layers.Add(layer);
// Indicate that the list of layers has been modified and the unmanaged data needs to be reallocated.
m_layersChanged = true;
return layer;
}
/// <summary>
/// Add a new LayerQuad layer to the end of the list of layers.
/// </summary>
/// <returns>Added layer.</returns>
public LayerQuad AddLayerQuadHeadLocked()
{
if(Disposed)
throw new ObjectDisposedException("Layers");
LayerQuad layer = new LayerQuad();
layer.Header.Type = OVRTypes.LayerType.Quad;
layer.Header.Flags = OVRTypes.LayerFlags.HeadLocked;
m_layers.Add(layer);
// Indicate that the list of layers has been modified and the unmanaged data needs to be reallocated.
m_layersChanged = true;
return layer;
}
/// <summary>
/// Add a new LayerQuad layer to the end of the list of layers.
/// </summary>
/// <returns>Added layer.</returns>
public LayerQuad AddLayerQuadInWorld()
{
if(Disposed)
throw new ObjectDisposedException("Layers");
LayerQuad layer = new LayerQuad();
layer.Header.Type = OVRTypes.LayerType.Quad;
m_layers.Add(layer);
// Indicate that the list of layers has been modified and the unmanaged data needs to be reallocated.
m_layersChanged = true;
return layer;
}
/// <summary>
/// Remove the layer at the specified index.
/// </summary>
/// <param name="index">Index of the layer to remove.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the specified index is out of range.</exception>
public void Remove(int index)
{
if(Disposed)
throw new ObjectDisposedException("Layers");
if(index >= m_layers.Count)
throw new ArgumentOutOfRangeException("index", "Unable to remove layer index "+index+". Only "+m_layers.Count+" layers exist.");
m_layers.RemoveAt(index);
// Indicate that the list of layers has been modified and the unmanaged data needs to be reallocated.
m_layersChanged = true;
}
/// <summary>
/// Retrieves the layer at the specified index.
/// </summary>
/// <param name="index">Index of the layer to get.</param>
/// <returns>Index at the specified layer.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the specified index is out of range.</exception>
public object this[int index]
{
get
{
if(Disposed)
throw new ObjectDisposedException("Layers");
if(index < 0)
throw new ArgumentOutOfRangeException("index", "Layer index "+index+" does not exist.");
if(index >= m_layers.Count)
throw new ArgumentOutOfRangeException("index", "Unable to remove layer index "+index+". Only "+m_layers.Count+" layers exist.");
return m_layers[index];
}
}
/// <summary>
/// Returns the number of layers available.
/// </summary>
public int Count
{
get
{
if(Disposed)
throw new ObjectDisposedException("Layers");
return m_layers.Count;
}
}
/// <summary>
/// Retrieves the list of unmanaged layer data, to be passed to ovrHmd_SubmitFrame.
/// </summary>
/// <returns>List of unmanaged layer data.</returns>
public IntPtr GetUnmanagedLayers()
{
if(Disposed)
throw new ObjectDisposedException("Layers");
// If the number of layers have changed, since the last time the unmanaged layers were retrieved.
if(m_layersChanged)
{
AllocateUnmanaged();
m_layersChanged = false;
}
// Copy values from the list of layers to the list of unmanaged layer data.
CopyToUnmanaged();
return m_layerArrayData;
}
#endregion
#region Private methods
/// <summary>
/// Allocate unmanaged memory for each layer.
///
/// Because each layer may be of a different type, the size of each layer may be different.
/// </summary>
private void AllocateUnmanaged()
{
// Clean up previously allocated layers.
DeallocateUnmanaged();
if(m_layers.Count > 0)
{
// Allocate memory for an unmanaged copy of the pointers to the layers of unmanaged data.
int pointerSize = Marshal.SizeOf(typeof(IntPtr));
m_layerArrayData = Marshal.AllocHGlobal(pointerSize*m_layers.Count);
// Allocate unmanaged data for each layer.
for (int count=0; count<m_layers.Count; count++)
{
// If the layer is disabled, it will contain a null layer.
if(m_layers[count] == null)
{
m_layerData.Add(IntPtr.Zero);
continue;
}
//Type layerType = m_layers[count].GetType();
Type layerType = m_layers[count].LayerType;
int layerSize = Marshal.SizeOf(layerType);
IntPtr layerData = Marshal.AllocHGlobal(layerSize);
// Store the unmanaged memory needed to contain the current layer.
m_layerData.Add(layerData);
// Write the pointer, to the current layer, to the unmanaged layer array.
Marshal.WriteIntPtr(m_layerArrayData, pointerSize*count, layerData);
}
}
}
/// <summary>
/// Clean up previously allocated layers.
/// </summary>
private void DeallocateUnmanaged()
{
// Deallocate previously allocated unmanaged layer data.
foreach(IntPtr layerData in m_layerData)
{
if(layerData != IntPtr.Zero)
Marshal.FreeHGlobal(layerData);
}
if(m_layerArrayData != IntPtr.Zero)
{
// Deallocate the unmanaged copy of the pointers to unmanaged layer data.
Marshal.FreeHGlobal(m_layerArrayData);
m_layerArrayData = IntPtr.Zero;
}
m_layerData.Clear();
}
/// <summary>
/// Copies data from the list of layers to the list of unmanaged layer data.
/// </summary>
private void CopyToUnmanaged()
{
for(int layerIndex=0; layerIndex<m_layerData.Count; layerIndex++)
Marshal.StructureToPtr(m_layers[layerIndex].Layer, m_layerData[layerIndex], false);
}
#endregion
#region Private fields
private List<ILayer> m_layers = new List<ILayer>();
private List<IntPtr> m_layerData = new List<IntPtr>();
private IntPtr m_layerArrayData = IntPtr.Zero;
private bool m_layersChanged = false;
#endregion
}
/// <summary>
/// Defines properties shared by all ovrLayer structs, such as LayerEyeFov.
///
/// LayerHeader is used as a base member in these larger structs.
/// This struct cannot be used by itself except for the case that Type is LayerType_Disabled.
/// </summary>
/// <see cref="OVRTypes.LayerType"/>
/// <see cref="OVRTypes.LayerFlags"/>
public class LayerHeader
{
/// <summary>
/// Described by LayerType.
/// </summary>
public OVRTypes.LayerType Type
{
get;
set;
}
/// <summary>
/// Described by LayerFlags.
/// </summary>
public OVRTypes.LayerFlags Flags
{
get;
set;
}
}
/// <summary>
/// Defines the methods needed to convert the managed class to a struct, which can be marshaled to unmanaged memory.
/// </summary>
public interface ILayer
{
/// <summary>
/// Retrieve the layer structure, which is needed to marshal the managed structure to unmanaged memory.
/// </summary>
/// <returns>Layer sturcture needed to marshal the managed structure to unmanaged memory.</returns>
object Layer
{
get;
}
/// <summary>
/// Retrieves the type of layer returned by the call to the GetLayer method.
/// </summary>
Type LayerType
{
get;
}
}
/// <summary>
/// Describes a layer that specifies a monoscopic or stereoscopic view.
///
/// This is the kind of layer that's typically used as layer 0 to Hmd.SubmitFrame,
/// as it is the kind of layer used to render a 3D stereoscopic view.
///
/// Three options exist with respect to mono/stereo texture usage:
/// - ColorTexture[0] and ColorTexture[1] contain the left and right stereo renderings, respectively.
/// Viewport[0] and Viewport[1] refer to ColorTexture[0] and ColorTexture[1], respectively.
/// - ColorTexture[0] contains both the left and right renderings, ColorTexture[1] is NULL,
/// and Viewport[0] and Viewport[1] refer to sub-rects with ColorTexture[0].
/// - ColorTexture[0] contains a single monoscopic rendering, and Viewport[0] and
/// Viewport[1] both refer to that rendering.
/// </summary>
/// <see cref="Hmd.SubmitFrame(uint, Layers)"/>
/// <see cref="Hmd.SubmitFrame(uint, OVRTypes.ViewScaleDesc, Layers)"/>
public class LayerEyeFov :ILayer
{
/// <summary>
/// Creates a new LayerEyeFov.
/// </summary>
public LayerEyeFov()
{
Header = new LayerHeader();
Header.Type = OVRTypes.LayerType.EyeFov;
ColorTexture = new IntPtr[2];
Viewport = new OVRTypes.Recti[2];
Fov = new OVRTypes.FovPort[2];
RenderPose = new OVRTypes.Posef[2];
}
#region ILayer implementation
/// <summary>
/// Retrieve the OVRTypes.LayerEyeFov, which is needed to marshal the managed structure to unmanaged memory.
/// </summary>
/// <returns>OVRTypes.LayerEyeFov containing the same values as this LayerEyeFov.</returns>
object ILayer.Layer
{
get
{
OVRTypes.LayerEyeFov layer = new OVRTypes.LayerEyeFov();
layer.Header.Type = this.Header.Type;
layer.Header.Flags = this.Header.Flags;
layer.ColorTexture = this.ColorTexture;
layer.Viewport = this.Viewport;
layer.Fov = this.Fov;
layer.RenderPose = this.RenderPose;
layer.SensorSampleTime = this.SensorSampleTime;
return layer;
}
}
/// <summary>
/// Retrieves the type of layer returned by the call to the GetLayer method.
/// </summary>
Type ILayer.LayerType
{
get
{
return typeof(OVRTypes.LayerEyeFov);
}
}
#endregion
#region Public properties
/// <summary>
/// Header.Type must be LayerType_EyeFov.
/// </summary>
public LayerHeader Header
{
get;
set;
}
/// <summary>
/// TextureSwapChains for the left and right eye respectively.
///
/// The second one of which can be null for cases described above.
/// </summary>
public ovrTextureSwapChain[] ColorTexture
{
get;
set;
}
/// <summary>
/// Specifies the ColorTexture sub-rect UV coordinates.
///
/// Both Viewport[0] and Viewport[1] must be valid.
/// </summary>
public OVRTypes.Recti[] Viewport
{
get;
set;
}
/// <summary>
/// The viewport field of view.
/// </summary>
public OVRTypes.FovPort[] Fov
{
get;
set;
}
/// <summary>
/// Specifies the position and orientation of each eye view, with the position specified in meters.
/// RenderPose will typically be the value returned from ovr_CalcEyePoses,
/// but can be different in special cases if a different head pose is used for rendering.
/// </summary>
public OVRTypes.Posef[] RenderPose
{
get;
set;
}
/// <summary>
/// Specifies the timestamp when the source ovrPosef (used in calculating RenderPose)
/// was sampled from the SDK. Typically retrieved by calling ovr_GetTimeInSeconds
/// around the instant the application calls ovr_GetTrackingState
/// The main purpose for this is to accurately track app tracking latency.
/// </summary>
public double SensorSampleTime
{
get;
set;
}
#endregion
}
/// <summary>
/// Describes a layer that specifies a monoscopic or stereoscopic view.
/// This uses a direct 3x4 matrix to map from view space to the UV coordinates.
/// It is essentially the same thing as ovrLayerEyeFov but using a much
/// lower level. This is mainly to provide compatibility with specific apps.
/// Unless the application really requires this flexibility, it is usually better
/// to use ovrLayerEyeFov.
///
/// Three options exist with respect to mono/stereo texture usage:
/// - ColorTexture[0] and ColorTexture[1] contain the left and right stereo renderings, respectively.
/// Viewport[0] and Viewport[1] refer to ColorTexture[0] and ColorTexture[1], respectively.
/// - ColorTexture[0] contains both the left and right renderings, ColorTexture[1] is null,
/// and Viewport[0] and Viewport[1] refer to sub-rects with ColorTexture[0].
/// - ColorTexture[0] contains a single monoscopic rendering, and Viewport[0] and
/// Viewport[1] both refer to that rendering.
/// </summary>
/// <see cref="Hmd.SubmitFrame(uint, Layers)"/>
/// <see cref="Hmd.SubmitFrame(uint, OVRTypes.ViewScaleDesc, Layers)"/>
public class LayerEyeMatrix :ILayer
{
/// <summary>
/// Creates a new LayerEyeMatrix.
/// </summary>
public LayerEyeMatrix()
{
Header = new LayerHeader();
Header.Type = OVRTypes.LayerType.EyeMatrix;
ColorTexture = new IntPtr[2];
Viewport = new OVRTypes.Recti[2];
RenderPose = new OVRTypes.Posef[2];
Matrix = new OVRTypes.Matrix4f[2];
}
#region ILayer implementation
/// <summary>
/// Retrieve the OVRTypes.LayerEyeFov, which is needed to marshal the managed structure to unmanaged memory.
/// </summary>
/// <returns>OVRTypes.LayerEyeFov containing the same values as this LayerEyeFov.</returns>
object ILayer.Layer
{
get
{
OVRTypes.LayerEyeMatrix layer = new OVRTypes.LayerEyeMatrix();
layer.Header.Type = this.Header.Type;
layer.Header.Flags = this.Header.Flags;
layer.ColorTexture = this.ColorTexture;
layer.Viewport = this.Viewport;
layer.RenderPose = this.RenderPose;
layer.Matrix = this.Matrix;
layer.SensorSampleTime = this.SensorSampleTime;
return layer;
}
}
/// <summary>
/// Retrieves the type of layer returned by the call to the GetLayer method.
/// </summary>
Type ILayer.LayerType
{
get
{
return typeof(OVRTypes.LayerEyeMatrix);
}
}
#endregion
#region Public properties
/// <summary>
/// Header.Type must be LayerType_EyeFov.
/// </summary>
public LayerHeader Header
{
get;
set;
}
/// <summary>
/// ovrTextureSwapChains for the left and right eye respectively.
///
/// The second one of which can be null for cases described above.
/// </summary>
public ovrTextureSwapChain[] ColorTexture
{
get;
set;
}
/// <summary>
/// Specifies the ColorTexture sub-rect UV coordinates.
///
/// Both Viewport[0] and Viewport[1] must be valid.
/// </summary>
public OVRTypes.Recti[] Viewport
{
get;
set;
}
/// <summary>
/// Specifies the position and orientation of each eye view, with the position specified in meters.
/// RenderPose will typically be the value returned from ovr_CalcEyePoses,
/// but can be different in special cases if a different head pose is used for rendering.
/// </summary>
public OVRTypes.Posef[] RenderPose
{
get;
set;
}
/// <summary>
/// Specifies the mapping from a view-space vector
/// to a UV coordinate on the textures given above.
/// P = (x,y,z,1)*Matrix
/// TexU = P.x/P.z
/// TexV = P.y/P.z
/// </summary>
public OVRTypes.Matrix4f[] Matrix
{
get;
set;
}
/// <summary>
/// Specifies the timestamp when the source OVRTypes.Posef (used in calculating RenderPose)
/// was sampled from the SDK. Typically retrieved by calling ovr_GetTimeInSeconds
/// around the instant the application calls ovr_GetTrackingState
/// The main purpose for this is to accurately track app tracking latency.
/// </summary>
public double SensorSampleTime
{
get;
set;
}
#endregion
}
/// <summary>
/// Describes a layer of Quad type, which is a single quad in world or viewer space.
/// It is used for both ovrLayerType_QuadInWorld and ovrLayerType_QuadHeadLocked.
/// This type of layer represents a single object placed in the world and not a stereo
/// view of the world itself.
///
/// A typical use of ovrLayerType_QuadInWorld is to draw a television screen in a room
/// that for some reason is more convenient to draw as a layer than as part of the main
/// view in layer 0. For example, it could implement a 3D popup GUI that is drawn at a
/// higher resolution than layer 0 to improve fidelity of the GUI.
///
/// A use of ovrLayerType_QuadHeadLocked might be to implement a debug HUD visible in
/// the HMD.
///
/// Quad layers are visible from both sides; they are not back-face culled.
/// </summary>
/// <see cref="Hmd.SubmitFrame(uint, Layers)"/>
/// <see cref="Hmd.SubmitFrame(uint, OVRTypes.ViewScaleDesc, Layers)"/>
public class LayerQuad :ILayer
{
/// <summary>
/// Creates a new LayerQuad.
/// </summary>
public LayerQuad()
{
Header = new LayerHeader();
Header.Type = OVRTypes.LayerType.Quad;
ColorTexture = IntPtr.Zero;
Viewport = new OVRTypes.Recti();
QuadPoseCenter = new OVRTypes.Posef();
QuadSize = new OVRTypes.Vector2f();
}
#region ILayer implementation
/// <summary>
/// Retrieve the OVRTypes.LayerEyeFov, which is needed to marshal the managed structure to unmanaged memory.
/// </summary>
/// <returns>OVRTypes.LayerEyeFov containing the same values as this LayerEyeFov.</returns>
object ILayer.Layer
{
get
{
OVRTypes.LayerQuad layer = new OVRTypes.LayerQuad();
layer.Header.Type = this.Header.Type;
layer.Header.Flags = this.Header.Flags;
layer.ColorTexture = this.ColorTexture;
layer.Viewport = this.Viewport;
layer.QuadPoseCenter = this.QuadPoseCenter;
layer.QuadSize = this.QuadSize;
return layer;
}
}
/// <summary>
/// Retrieves the type of layer returned by the call to the GetLayer method.
/// </summary>
Type ILayer.LayerType
{
get
{
return typeof(OVRTypes.LayerQuad);
}
}
#endregion
#region Public properties
/// <summary>
/// Header.Type must be LayerType.Quad.
/// </summary>
public LayerHeader Header
{
get;
set;
}
/// <summary>
/// Contains a single image, never with any stereo view.
/// </summary>
public ovrTextureSwapChain ColorTexture
{
get;
set;
}
/// <summary>
/// Specifies the ColorTexture sub-rect UV coordinates.
/// </summary>
public OVRTypes.Recti Viewport
{
get;
set;
}
/// <summary>
/// Specifies the orientation and position of the center point of a Quad layer type.
/// The supplied direction is the vector perpendicular to the quad.
/// The position is in real-world meters (not the application's virtual world,
/// the physical world the user is in) and is relative to the "zero" position
/// set by ovr_RecenterTrackingOrigin unless the ovrLayerFlag_HeadLocked flag is used.
/// </summary>
public OVRTypes.Posef QuadPoseCenter
{
get;
set;
}
/// <summary>
/// Width and height (respectively) of the quad in meters.
/// </summary>
public OVRTypes.Vector2f QuadSize
{
get;
set;
}
#endregion
}
}