上传YomovSDK

This commit is contained in:
Sora丶kong
2026-03-03 03:15:46 +08:00
parent 9096da7e6c
commit eb97f31065
6477 changed files with 1932208 additions and 3 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9b16e408c09007b46bddec7439822d52
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
# 12.89. XR_HTC_passthrough
## Name String
XR_HTC_passthrough
## Revision
1
## New Object Types
- [XrPassthroughHTC](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XrPassthroughHTC)
## New Enum Constants
[XrObjectType](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XrObjectType) enumeration is extended with:
- XR_OBJECT_TYPE_PASSTHROUGH_HTC
[XrStructureType](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XrStructureType) enumeration is extended with:
- XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC
- XR_TYPE_PASSTHROUGH_COLOR_HTC
- XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC
- XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC
## New Enums
- [XrPassthroughFormHTC](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XrPassthroughFormHTC)
## New Structures
- [XrPassthroughCreateInfoHTC](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XrPassthroughCreateInfoHTC)
- [XrPassthroughColorHTC](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XrPassthroughColorHTC)
- [XrPassthroughMeshTransformInfoHTC](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XrPassthroughMeshTransformInfoHTC)
- [XrCompositionLayerPassthroughHTC](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XrCompositionLayerPassthroughHTC)
## New Functions
- [xrCreatePassthroughHTC](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#xrCreatePassthroughHTC)
- [xrDestroyPassthroughHTC](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#xrDestroyPassthroughHTC)
## VIVE Plugin
Enable "VIVE XR Passthrough" in "Project Settings > XR Plugin-in Management > OpenXR > Android Tab > OpenXR Feature Groups" to use the Passthrough feature provided by VIVE OpenXR plugin.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0fddf83b59e7c194493074db7cc7aebb
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a624175b30ff07b47920ec9a9699a1ea
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,989 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using UnityEditor;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
using UnityEngine;
using UnityEngine.XR;
using VIVE.OpenXR.CompositionLayer;
using UnityEngine.Profiling;
using VIVE.OpenXR.Feature;
#if UNITY_EDITOR
using UnityEditor.XR.OpenXR.Features;
#endif
namespace VIVE.OpenXR.Passthrough
{
public class PassthroughLayer : IDisposable
{
private XrPassthroughHTC xrHandle = 0;
private XrCompositionLayerPassthroughHTC layer;
private IntPtr layerPtr = IntPtr.Zero;
private XrPassthroughMeshTransformInfoHTC meshTransform;
private bool needMesh = false;
private IntPtr meshPtr = IntPtr.Zero;
private CompositionLayer.LayerType layerType = CompositionLayer.LayerType.Underlay;
private bool disposedValue = false;
private IntPtr verticesPtr = IntPtr.Zero;
private IntPtr indicesPtr = IntPtr.Zero;
private int depth = 0;
public bool NeedMesh { get => needMesh; set => needMesh = value; }
public CompositionLayer.LayerType LayerType { get => layerType; set => layerType = value; }
public int Depth { get => depth; set => depth = value; }
public PassthroughLayer(XrPassthroughHTC xrHandle, CompositionLayer.LayerType layerType)
{
this.xrHandle = xrHandle;
this.layerType = layerType;
layer = new XrCompositionLayerPassthroughHTC();
layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC)));
meshTransform = new XrPassthroughMeshTransformInfoHTC();
meshPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrPassthroughMeshTransformInfoHTC)));
}
public PassthroughLayer()
{
layer = new XrCompositionLayerPassthroughHTC();
layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC)));
meshTransform = new XrPassthroughMeshTransformInfoHTC();
meshPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrPassthroughMeshTransformInfoHTC)));
}
public void SetLayer(XrCompositionLayerPassthroughHTC layer)
{
this.layer = layer;
}
public static XrCompositionLayerPassthroughHTC MakeEmptyLayer()
{
XrPassthroughColorHTC passthroughColor = new XrPassthroughColorHTC(in_alpha: 0);
XrCompositionLayerPassthroughHTC compositionLayerPassthrough = new XrCompositionLayerPassthroughHTC(
in_layerFlags: (UInt64)XrCompositionLayerFlagBits.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT,
in_space: 0,
in_passthrough: 0,
in_color: passthroughColor);
return compositionLayerPassthrough;
}
public XrCompositionLayerPassthroughHTC GetLayer()
{
return layer;
}
/// <summary>
/// Never null unless disposed.
/// </summary>
/// <returns></returns>
public IntPtr GetLayerPtr()
{
return layerPtr;
}
/// <summary>
/// Before SetMeshTransform, you should call SetLayer first.
/// </summary>
/// <param name="meshTransform"></param>
/// <param name="needMesh"></param>
public void SetMeshTransform(XrPassthroughMeshTransformInfoHTC meshTransform, bool needMesh = true)
{
this.meshTransform = meshTransform;
NeedMesh = needMesh;
}
public static XrPassthroughMeshTransformInfoHTC MakeMeshTransform()
{
return new XrPassthroughMeshTransformInfoHTC()
{
type = XrStructureType.XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC,
next = IntPtr.Zero,
vertexCount = 0,
vertices = IntPtr.Zero,
indexCount = 0,
indices = IntPtr.Zero,
baseSpace = 0,
time = 0,
pose = XrPosef.Identity,
scale = XrVector3f.One,
};
}
public bool SetMeshData(ref XrPassthroughMeshTransformInfoHTC mesh, Vector3[] vertices, int[] indices, bool convertFromUnityToOpenXR = true)
{
if (vertices.Length < 3 || indices.Length % 3 != 0) //Must have at least 3 vertices and complete triangles
{
Debug.LogError("PassthroughLayer: Mesh data invalid.");
return false;
}
// check our vertex buffer and index buffer
if (verticesPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(verticesPtr);
verticesPtr = IntPtr.Zero;
}
if (indicesPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(indicesPtr);
indicesPtr = IntPtr.Zero;
}
verticesPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrVector3f)) * vertices.Length);
indicesPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint)) * indices.Length);
XrVector3f[] xrVertices = new XrVector3f[vertices.Length];
for (int i = 0; i < vertices.Length; i++)
{
xrVertices[i] = OpenXRHelper.ToOpenXRVector(vertices[i], convertFromUnityToOpenXR);
}
uint[] indicesUint = new uint[indices.Length];
// Unity is contrary to OpenXR in the order of the vertices in the triangle.
for (int i = 0; i < indices.Length; i += 3)
{
indicesUint[i] = (uint)indices[i];
indicesUint[i + 1] = (uint)indices[i + 2];
indicesUint[i + 2] = (uint)indices[i + 1];
}
MemoryTools.CopyToRawMemory(verticesPtr, xrVertices);
MemoryTools.CopyToRawMemory(indicesPtr, indicesUint);
mesh.vertexCount = (uint)vertices.Length;
mesh.vertices = verticesPtr;
mesh.indexCount = (uint)indices.Length;
mesh.indices = indicesPtr;
return true;
}
/// <summary>
/// Copy to native buffer
/// </summary>
public void ToNativeBuffer()
{
if (layerPtr == IntPtr.Zero)
return;
if (NeedMesh)
{
layer.next = meshPtr;
MemoryTools.StructureToPtr(meshTransform, meshPtr);
MemoryTools.StructureToPtr(layer, layerPtr);
}
else
{
if (layer.next != IntPtr.Zero)
layer.next = IntPtr.Zero;
MemoryTools.StructureToPtr(layer, layerPtr);
}
}
public XrPassthroughMeshTransformInfoHTC GetMesh()
{
return meshTransform;
}
public IntPtr GetMeshTransformPtr()
{
return meshPtr;
}
public bool IsOverlay()
{
return layerType == CompositionLayer.LayerType.Overlay;
}
public bool IsUnderlay()
{
return layerType == CompositionLayer.LayerType.Underlay;
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
xrHandle = 0;
}
if (layerPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(layerPtr);
layerPtr = IntPtr.Zero;
}
if (meshPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(meshPtr);
meshPtr = IntPtr.Zero;
}
if (verticesPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(verticesPtr);
verticesPtr = IntPtr.Zero;
}
if (indicesPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(indicesPtr);
indicesPtr = IntPtr.Zero;
}
disposedValue = true;
}
}
~PassthroughLayer()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: false);
}
void IDisposable.Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
internal void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
#if UNITY_EDITOR
[OpenXRFeature(UiName = "VIVE XR Passthrough",
Desc = "Enable this feature to use the VIVE OpenXR Passthrough feature.",
Company = "HTC",
DocumentationLink = "..\\Documentation",
OpenxrExtensionStrings = kOpenxrExtensionStrings,
Version = "1.0.0",
BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone },
FeatureId = featureId
)]
#endif
public class VivePassthrough : OpenXRFeature
{
#region LOG
const string TAG = "VivePassthrough";
StringBuilder sb = new StringBuilder();
StringBuilder CSB {
get {
return sb.Clear();
}
}
#endregion
/// <summary>
/// The feature id string. This is used to give the feature a well known id for reference.
/// </summary>
public const string featureId = "vive.openxr.feature.passthrough";
/// <summary>
/// The extension string.
/// </summary>
public const string kOpenxrExtensionStrings = "XR_HTC_passthrough XR_HTC_passthrough_configuration";
private static IntPtr[] layersOrigin = null;
private static IntPtr[] layersModified = null;
private static int sizeOfIntPtr = Marshal.SizeOf(typeof(IntPtr));
private static IntPtr layersModifiedPtr = Marshal.AllocHGlobal(sizeOfIntPtr * 30); //Preallocate a layer buffer with sufficient size and reuse it for each frame.
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
{
var interceptors = ViveInterceptors.Instance;
interceptors.AddRequiredFunction("xrWaitFrame");
interceptors.AddRequiredFunction("xrEndFrame");
return interceptors.HookGetInstanceProcAddr(func);
}
struct XrCompositionLayerProjection
{
public XrStructureType type;
public IntPtr next;
public XrCompositionLayerFlags layerFlags;
public XrSpace space;
public uint viewCount;
public IntPtr views;
}
private void ForceProjectionLayerTransparent(IntPtr[] layersPtr)
{
// Find the projection layer in freameEndInfo
int projIndex = -1;
IntPtr projLayerPtr = IntPtr.Zero;
for (int i = 0; i < layersPtr.Length; i++)
{
projLayerPtr = layersPtr[i];
if (MemoryTools.GetType(projLayerPtr) == XrStructureType.XR_TYPE_COMPOSITION_LAYER_PROJECTION)
{
projIndex = i;
break;
}
}
// No projection layer found.
if (projIndex == -1)
Debug.Log("No projection layer");
// Force projection layer accept transparent
XrCompositionLayerProjection xrProjLayer = default;
MemoryTools.PtrToStructure(projLayerPtr, ref xrProjLayer);
xrProjLayer.layerFlags |= ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
MemoryTools.StructureToPtr(xrProjLayer, projLayerPtr);
}
private bool OnBeforeEndFrame(XrSession session, ref ViveInterceptors.XrFrameEndInfo frameEndInfo, ref XrResult result)
{
// It is possible that the layerCount is 0, so we need to check it.
if (frameEndInfo.layerCount == 0 || ptLayers == null || ptLayers.Count == 0)
return true;
uint layerCount = frameEndInfo.layerCount;
Profiler.BeginSample("Pt");
// Make all layers to array
if (layersOrigin == null || layersOrigin.Length != layerCount)
layersOrigin = new IntPtr[layerCount];
MemoryTools.CopyAllFromRawMemory(layersOrigin, frameEndInfo.layers);
// Insert our layers into layersOrigin
// Total passthough layers count
var finalCount = ptLayers.Count + layerCount;
if (layersModified == null || layersModified.Length != finalCount)
layersModified = new IntPtr[finalCount];
int j = 0;
bool hasUnderlay = false;
// Insert underlay
for (int i = 0; i < ptLayers.Count; i++)
{
if (ptLayers[i].IsUnderlay())
{
ptLayers[i].ToNativeBuffer();
var ptr = ptLayers[i].GetLayerPtr();
if (ptr != IntPtr.Zero)
layersModified[j++] = ptr;
hasUnderlay = true;
}
}
if (hasUnderlay)
ForceProjectionLayerTransparent(layersOrigin);
// put original layers into layersModified
for (int i = 0; i < layersOrigin.Length; i++)
{
layersModified[j++] = layersOrigin[i];
}
// Append overlay
for (int i = 0; i < ptLayers.Count; i++)
{
if (ptLayers[i].IsOverlay())
{
ptLayers[i].ToNativeBuffer();
var ptr = ptLayers[i].GetLayerPtr();
if (ptr != IntPtr.Zero)
layersModified[j++] = ptr;
}
}
MemoryTools.CopyToRawMemory(layersModifiedPtr, layersModified);
// Change original layers
frameEndInfo.layers = layersModifiedPtr;
frameEndInfo.layerCount = (uint)layersModified.Length;
Profiler.EndSample();
return true;
}
List<PassthroughLayer> ptLayers = null;
/// <summary>
/// Call this function in game thread
/// </summary>
/// <param name="layers">passthrough layers</param>
public void SubmitLayers(List<PassthroughLayer> layers)
{
ptLayers = layers;
}
public XrFrameState GetFrameState()
{
var frameState = ViveInterceptors.Instance.GetCurrentFrameState();
return new XrFrameState() {
predictedDisplayTime = frameState.predictedDisplayTime,
predictedDisplayPeriod = frameState.predictedDisplayPeriod,
shouldRender = frameState.shouldRender
};
}
#region OpenXR Life Cycle
private bool m_XrInstanceCreated = false;
/// <summary>
/// The XR instance is created or not.
/// </summary>
public bool XrInstanceCreated
{
get { return m_XrInstanceCreated; }
}
private XrInstance m_XrInstance = 0;
protected override bool OnInstanceCreate(ulong xrInstance)
{
Log.D(TAG, "OnInstanceCreate() " + m_XrInstance);
foreach (string kOpenxrExtensionString in kOpenxrExtensionStrings.Split(' '))
{
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
{
Log.W(TAG, "OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
}
else
{
Log.I(TAG, "OnInstanceCreate() " + kOpenxrExtensionString + " is enabled.");
}
}
SpaceWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
CommonWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
bool ret = GetXrFunctionDelegates(xrInstance);
if (!ret)
return ret;
m_XrInstanceCreated = true;
m_XrInstance = xrInstance;
ViveInterceptors.Instance.BeforeOriginalEndFrame += OnBeforeEndFrame;
return ret;
}
protected override void OnInstanceDestroy(ulong xrInstance)
{
if (m_XrInstanceCreated)
{
ViveInterceptors.Instance.BeforeOriginalEndFrame -= OnBeforeEndFrame;
CommonWrapper.Instance.OnInstanceDestroy();
SpaceWrapper.Instance.OnInstanceDestroy();
}
m_XrInstanceCreated = false;
Log.D(TAG, "OnInstanceDestroy() " + m_XrInstance);
}
private XrSystemId m_XrSystemId = 0;
protected override void OnSystemChange(ulong xrSystem)
{
m_XrSystemId = xrSystem;
Log.D(TAG, "OnSystemChange() " + m_XrSystemId);
}
private bool m_XrSessionCreated = false;
/// <summary>
/// The XR session is created or not.
/// </summary>
public bool XrSessionCreated
{
get { return m_XrSessionCreated; }
}
private XrSession m_XrSession = 0;
protected override void OnSessionCreate(ulong xrSession)
{
m_XrSession = xrSession;
m_XrSessionCreated = true;
Log.I(TAG, "OnSessionCreate() " + m_XrSession);
CheckConfigurationSupport();
}
private XrSpace m_WorldLockSpaceOriginOnHead = 0, m_WorldLockSpaceOriginOnFloor = 0, m_HeadLockSpace = 0;
private XrSpace WorldLockSpaceOriginOnHead
{
get { return m_WorldLockSpaceOriginOnHead; }
}
private XrSpace WorldLockSpaceOriginOnFloor
{
get { return m_WorldLockSpaceOriginOnFloor; }
}
private XrSpace HeadLockSpace
{
get { return m_HeadLockSpace; }
}
private bool m_XrSessionEnding = false;
/// <summary>
/// The XR session is ending or not.
/// </summary>
public bool XrSessionEnding
{
get { return m_XrSessionEnding; }
}
protected override void OnSessionBegin(ulong xrSession)
{
m_XrSessionEnding = false;
Log.D(TAG, "OnSessionBegin() " + m_XrSession);
// Enumerate supported reference space types and create the XrSpace.
XrReferenceSpaceType[] spaces = null;// new XrReferenceSpaceType[Enum.GetNames(typeof(XrReferenceSpaceType)).Count()];
int spaceCountOutput = 0;
if (SpaceWrapper.Instance.EnumerateReferenceSpaces(
xrSession,
spaceCapacityInput: 0,
spaceCountOutput: ref spaceCountOutput,
spaces: ref spaces) == XrResult.XR_SUCCESS)
{
//Log.I(TAG, "spaceCountOutput: " + spaceCountOutput);
Array.Resize(ref spaces, (int)spaceCountOutput);
if (SpaceWrapper.Instance.EnumerateReferenceSpaces(
xrSession,
spaceCapacityInput: spaceCountOutput,
spaceCountOutput: ref spaceCountOutput,
spaces: ref spaces) == XrResult.XR_SUCCESS)
{
if (spaces.Contains(XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_LOCAL))
{
XrReferenceSpaceCreateInfo referenceSpaceCreateInfoWorldLock;
referenceSpaceCreateInfoWorldLock.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
referenceSpaceCreateInfoWorldLock.next = IntPtr.Zero;
referenceSpaceCreateInfoWorldLock.referenceSpaceType = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_LOCAL;
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.orientation = new XrQuaternionf(0, 0, 0, 1);
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.position = new XrVector3f(0, 0, 0);
if (SpaceWrapper.Instance.CreateReferenceSpace(
xrSession,
createInfo: referenceSpaceCreateInfoWorldLock,
space: out m_WorldLockSpaceOriginOnHead) == XrResult.XR_SUCCESS)
{
//Log.I(TAG, "CreateReferenceSpace: " + m_WorldLockSpaceOriginOnHead);
}
else
{
Log.E(TAG, "CreateReferenceSpace for world lock layers on head failed.");
}
}
else
{
Log.E(TAG, "CreateReferenceSpace no space type for world lock on head layers.");
}
if (spaces.Contains(XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_STAGE))
{
XrReferenceSpaceCreateInfo referenceSpaceCreateInfoWorldLock;
referenceSpaceCreateInfoWorldLock.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
referenceSpaceCreateInfoWorldLock.next = IntPtr.Zero;
referenceSpaceCreateInfoWorldLock.referenceSpaceType = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_STAGE;
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.orientation = new XrQuaternionf(0, 0, 0, 1);
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.position = new XrVector3f(0, 0, 0);
if (SpaceWrapper.Instance.CreateReferenceSpace(
xrSession,
createInfo: referenceSpaceCreateInfoWorldLock,
space: out m_WorldLockSpaceOriginOnFloor) == XrResult.XR_SUCCESS)
{
//Log.I(TAG, "CreateReferenceSpace: " + m_WorldLockSpaceOriginOnFloor);
}
else
{
Log.E(TAG, "CreateReferenceSpace for world lock layers on floor failed.");
}
}
else
{
Log.E(TAG, "CreateReferenceSpace no space type for world lock on floor layers.");
}
if (spaces.Contains(XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_VIEW))
{
XrReferenceSpaceCreateInfo referenceSpaceCreateInfoHeadLock;
referenceSpaceCreateInfoHeadLock.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
referenceSpaceCreateInfoHeadLock.next = IntPtr.Zero;
referenceSpaceCreateInfoHeadLock.referenceSpaceType = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_VIEW;
referenceSpaceCreateInfoHeadLock.poseInReferenceSpace.orientation = new XrQuaternionf(0, 0, 0, 1);
referenceSpaceCreateInfoHeadLock.poseInReferenceSpace.position = new XrVector3f(0, 0, 0);
if (SpaceWrapper.Instance.CreateReferenceSpace(
xrSession,
createInfo: referenceSpaceCreateInfoHeadLock,
space: out m_HeadLockSpace) == XrResult.XR_SUCCESS)
{
//Log.I(TAG, "CreateReferenceSpace: " + m_HeadLockSpace);
}
else
{
Log.E(TAG, "CreateReferenceSpace for head lock layers failed.");
}
}
else
{
Log.E(TAG, "CreateReferenceSpace no space type for head lock layers.");
}
}
else
{
Log.E(TAG, "EnumerateReferenceSpaces(" + spaceCountOutput + ") failed.");
}
}
else
{
Log.E(TAG, "EnumerateReferenceSpaces(0) failed.");
}
}
protected override void OnSessionEnd(ulong xrSession)
{
m_XrSessionEnding = true;
Log.D(TAG, "OnSessionEnd() " + m_XrSession);
}
/// <summary>
/// The delegate of Passthrough Session Destroy.
/// </summary>
public delegate void OnPassthroughSessionDestroyDelegate(XrPassthroughHTC passthroughID);
private Dictionary<XrPassthroughHTC, OnPassthroughSessionDestroyDelegate> OnPassthroughSessionDestroyHandlerDictionary = new Dictionary<XrPassthroughHTC, OnPassthroughSessionDestroyDelegate>();
protected override void OnSessionDestroy(ulong xrSession)
{
if (!m_XrSessionCreated || m_XrSession != xrSession) { return; }
Log.D(TAG, CSB.Append("OnSessionDestroy() " + xrSession));
m_XrSessionCreated = false;
m_XrSession = 0;
List<XrPassthroughHTC> passthroughs = PassthroughList;
for (int i = 0; i < passthroughs.Count; i++)
{
XrPassthroughHTC currentpassthrough = passthroughs[i];
DestroyPassthroughHTC(currentpassthrough);
if (OnPassthroughSessionDestroyHandlerDictionary.ContainsKey(currentpassthrough) && OnPassthroughSessionDestroyHandlerDictionary[currentpassthrough] != null)
{
Log.D(TAG, CSB.Append("OnSessionDestroy() Call back ").Append(passthroughs[i]));
OnPassthroughSessionDestroyHandlerDictionary[passthroughs[i]].Invoke(passthroughs[i]);
}
}
if (m_HeadLockSpace != 0)
{
SpaceWrapper.Instance.DestroySpace(m_HeadLockSpace);
m_HeadLockSpace = 0;
}
if (m_WorldLockSpaceOriginOnFloor != 0)
{
SpaceWrapper.Instance.DestroySpace(m_WorldLockSpaceOriginOnFloor);
m_WorldLockSpaceOriginOnFloor = 0;
}
if (m_WorldLockSpaceOriginOnHead != 0)
{
SpaceWrapper.Instance.DestroySpace(m_WorldLockSpaceOriginOnHead);
m_WorldLockSpaceOriginOnHead = 0;
}
}
/// <summary>
/// The current XR session state.
/// </summary>
public XrSessionState XrSessionCurrentState
{
get { return m_XrSessionNewState; }
}
private XrSessionState m_XrSessionNewState = XrSessionState.XR_SESSION_STATE_UNKNOWN;
private XrSessionState m_XrSessionOldState = XrSessionState.XR_SESSION_STATE_UNKNOWN;
protected override void OnSessionStateChange(int oldState, int newState)
{
Log.D(TAG, "OnSessionStateChange() oldState: " + oldState + " newState:" + newState);
if (Enum.IsDefined(typeof(XrSessionState), oldState))
{
m_XrSessionOldState = (XrSessionState)oldState;
}
else
{
Log.I(TAG, "OnSessionStateChange() oldState undefined");
}
if (Enum.IsDefined(typeof(XrSessionState), newState))
{
m_XrSessionNewState = (XrSessionState)newState;
}
else
{
Log.I(TAG, "OnSessionStateChange() newState undefined");
}
}
#endregion
#region OpenXR function delegates
/// xrGetInstanceProcAddr
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
VivePassthroughHelper.xrCreatePassthroughHTCDelegate xrCreatePassthroughHTC;
VivePassthroughHelper.xrDestroyPassthroughHTCDelegate xrDestroyPassthroughHTC;
VivePassthroughHelper.xrEnumeratePassthroughImageRatesHTCDelegate xrEnumeratePassthroughImageRatesHTC;
VivePassthroughHelper.xrGetPassthroughConfigurationHTCDelegate xrGetPassthroughConfigurationHTC;
VivePassthroughHelper.xrSetPassthroughConfigurationHTCDelegate xrSetPassthroughConfigurationHTC;
private bool GetXrFunctionDelegates(XrInstance xrInstance)
{
/// xrGetInstanceProcAddr
if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero)
{
Log.I(TAG, "Get function pointer of xrGetInstanceProcAddr.");
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
xrGetInstanceProcAddr,
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
}
else
{
Log.E(TAG, "xrGetInstanceProcAddr");
return false;
}
bool ret = true;
IntPtr funcPtr = IntPtr.Zero;
ret &= OpenXRHelper.GetXrFunctionDelegate(XrGetInstanceProcAddr, xrInstance, "xrCreatePassthroughHTC", out xrCreatePassthroughHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(XrGetInstanceProcAddr, xrInstance, "xrDestroyPassthroughHTC", out xrDestroyPassthroughHTC);
#if UNITY_ANDROID
ret &= OpenXRHelper.GetXrFunctionDelegate(XrGetInstanceProcAddr, xrInstance, "xrEnumeratePassthroughImageRatesHTC", out xrEnumeratePassthroughImageRatesHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(XrGetInstanceProcAddr, xrInstance, "xrGetPassthroughConfigurationHTC", out xrGetPassthroughConfigurationHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(XrGetInstanceProcAddr, xrInstance, "xrSetPassthroughConfigurationHTC", out xrSetPassthroughConfigurationHTC);
#endif
return ret;
}
#endregion
private List<XrPassthroughHTC> passthroughList = new List<XrPassthroughHTC>();
public List<XrPassthroughHTC> PassthroughList {
get {
if (passthroughList == null) { passthroughList = new List<XrPassthroughHTC>(); }
return passthroughList;
}
}
#region Public API
public XrResult CreatePassthroughHTC(XrPassthroughCreateInfoHTC createInfo, out XrPassthroughHTC passthrough, CompositionLayer.LayerType layerType, uint compositionDepth, OnPassthroughSessionDestroyDelegate onDestroy)
{
passthrough = 0;
if (!m_XrSessionCreated)
{
Log.E(TAG, "CreatePassthroughHTC() XR_ERROR_SESSION_LOST.");
return XrResult.XR_ERROR_SESSION_LOST;
}
if (!m_XrInstanceCreated)
{
Log.E(TAG, "CreatePassthroughHTC() XR_ERROR_INSTANCE_LOST.");
return XrResult.XR_ERROR_INSTANCE_LOST;
}
Log.I(TAG, CSB.Append("CreatePassthroughHTC() layerType: ").Append(layerType).Append(", compositionDepth: ").Append(compositionDepth));
XrResult result = XrResult.XR_ERROR_RUNTIME_FAILURE;
result = xrCreatePassthroughHTC(m_XrSession, createInfo, out passthrough);
Log.I(TAG, "CreatePassthroughHTC() xrCreatePassthroughHTC result: " + result);
if (result == XrResult.XR_SUCCESS)
{
passthroughList.Add(passthrough);
if (onDestroy != null) { OnPassthroughSessionDestroyHandlerDictionary.Add(passthrough, onDestroy); }
}
return result;
}
public XrResult CreatePassthroughHTC(XrPassthroughCreateInfoHTC createInfo, out XrPassthroughHTC passthrough, CompositionLayer.LayerType layerType, OnPassthroughSessionDestroyDelegate onDestroy = null)
{
return CreatePassthroughHTC(createInfo, out passthrough, layerType, 0, onDestroy);
}
public XrResult CreatePassthroughHTC(XrPassthroughCreateInfoHTC createInfo, out XrPassthroughHTC passthrough, CompositionLayer.LayerType layerType = CompositionLayer.LayerType.Overlay)
{
return CreatePassthroughHTC(createInfo, out passthrough, layerType, 0, null);
}
public XrResult DestroyPassthroughHTC(XrPassthroughHTC passthrough)
{
if (!passthroughList.Contains(passthrough))
{
Log.E(TAG, "DestroyPassthroughHTC() Invalid passthrough: " + passthrough);
return XrResult.XR_ERROR_VALIDATION_FAILURE;
}
Log.I(TAG, CSB.Append("DestroyPassthroughHTC() passthrough: ").Append(passthrough));
XrResult result = XrResult.XR_ERROR_RUNTIME_FAILURE;
int passthroughID = (int)(passthrough & 0x00007FFF);
result = xrDestroyPassthroughHTC(passthrough);
Log.I(TAG, CSB.Append("DestroyPassthroughHTC() ").Append(passthrough).Append(", result: ").Append(result));
if (result == XrResult.XR_SUCCESS)
{
passthroughList.Remove(passthrough);
if (OnPassthroughSessionDestroyHandlerDictionary.ContainsKey(passthrough))
OnPassthroughSessionDestroyHandlerDictionary.Remove(passthrough);
}
return result;
}
/// <summary>
/// According to XRInputSubsystem's tracking origin mode, return the corresponding XrSpace.
/// </summary>
/// <returns><see cref="XrSpace"/> for tracking origin.</returns>
public XrSpace GetTrackingSpace()
{
XrSpace space = GetCurrentAppSpace();
Log.I(TAG, CSB.Append("GetTrackingSpace() ").Append(space));
return space;
}
private List<XRInputSubsystem> inputSubsystems = new List<XRInputSubsystem>();
/// <summary>
/// Helper function to get XrSpace from space type.
/// </summary>
public XrSpace GetXrSpaceFromSpaceType(ProjectedPassthroughSpaceType spaceType)
{
XrSpace meshSpace = 0;
switch (spaceType)
{
case ProjectedPassthroughSpaceType.Headlock:
meshSpace = HeadLockSpace;
break;
case ProjectedPassthroughSpaceType.Worldlock:
default:
XRInputSubsystem subsystem = null;
SubsystemManager.GetSubsystems(inputSubsystems);
if (inputSubsystems.Count > 0)
{
subsystem = inputSubsystems[0];
}
if (subsystem != null)
{
TrackingOriginModeFlags trackingOriginMode = subsystem.GetTrackingOriginMode();
switch (trackingOriginMode)
{
default:
case TrackingOriginModeFlags.Floor:
meshSpace = WorldLockSpaceOriginOnFloor;
break;
case TrackingOriginModeFlags.Device:
meshSpace = WorldLockSpaceOriginOnHead;
break;
}
}
else
{
meshSpace = WorldLockSpaceOriginOnFloor;
}
break;
}
return meshSpace;
}
public XrResult EnumeratePassthroughImageRatesHTC([In] UInt32 imageRateCapacityInput, ref UInt32 imageRateCountOutput,[In, Out] XrPassthroughConfigurationImageRateHTC[] imageRates)
{
if (!m_XrSessionCreated)
{
Log.E(TAG, "EnumeratePassthroughImageRatesHTC() XR_ERROR_SESSION_LOST.");
return XrResult.XR_ERROR_SESSION_LOST;
}
return xrEnumeratePassthroughImageRatesHTC(m_XrSession, imageRateCapacityInput, ref imageRateCountOutput, imageRates);
}
public XrResult GetPassthroughConfigurationHTC(IntPtr config)
{
if (!m_XrSessionCreated)
{
Log.E(TAG, "GetPassthroughConfigurationHTC() XR_ERROR_SESSION_LOST.");
return XrResult.XR_ERROR_SESSION_LOST;
}
return xrGetPassthroughConfigurationHTC(m_XrSession, config);
}
public XrResult SetPassthroughConfigurationHTC(IntPtr config)
{
if (!m_XrSessionCreated)
{
Log.E(TAG, "SetPassthroughConfigurationHTC() XR_ERROR_SESSION_LOST.");
return XrResult.XR_ERROR_SESSION_LOST;
}
return xrSetPassthroughConfigurationHTC(m_XrSession, config);
}
private XrBool32 m_SupportsImageRate;
private XrBool32 m_SupportsImageQuality;
XrSystemProperties systemProperties;
XrSystemPassthroughConfigurationPropertiesHTC passthroughConfigurationProperties;
private void CheckConfigurationSupport()
{
m_SupportsImageRate = false;
m_SupportsImageQuality = false;
if (!m_XrSessionCreated)
{
Log.E(TAG, "CheckUserPresenceSupport() session is not created.");
return;
}
passthroughConfigurationProperties.type = XrStructureType.XR_TYPE_SYSTEM_PASSTHROUGH_CONFIGURATION_PROPERTIES_HTC;
passthroughConfigurationProperties.next = IntPtr.Zero;
systemProperties.type = XrStructureType.XR_TYPE_SYSTEM_PROPERTIES;
systemProperties.next = Marshal.AllocHGlobal(Marshal.SizeOf(passthroughConfigurationProperties));
long offset = 0;
if (IntPtr.Size == 4)
offset = systemProperties.next.ToInt32();
else
offset = systemProperties.next.ToInt64();
IntPtr passthroughConfigurationPtr = new IntPtr(offset);
Marshal.StructureToPtr(passthroughConfigurationProperties, passthroughConfigurationPtr, false);
if (CommonWrapper.Instance.GetSystemProperties(m_XrInstance, m_XrSystemId, ref systemProperties) == XrResult.XR_SUCCESS)
{
if (IntPtr.Size == 4)
offset = systemProperties.next.ToInt32();
else
offset = systemProperties.next.ToInt64();
passthroughConfigurationPtr = new IntPtr(offset);
passthroughConfigurationProperties = (XrSystemPassthroughConfigurationPropertiesHTC)Marshal.PtrToStructure(passthroughConfigurationPtr, typeof(XrSystemPassthroughConfigurationPropertiesHTC));
Log.I(TAG, CSB.Append("CheckConfigurationSupport() supportsImageQuality: ").Append((UInt32)passthroughConfigurationProperties.supportsImageQuality));
Log.I(TAG, CSB.Append("CheckConfigurationSupport() supportsImageRate: ").Append((UInt32)passthroughConfigurationProperties.supportsImageRate));
m_SupportsImageQuality = passthroughConfigurationProperties.supportsImageQuality;
m_SupportsImageRate = passthroughConfigurationProperties.supportsImageRate;
}
else
{
Log.E(TAG, "CheckSupport() GetSystemProperties failed.");
}
Marshal.FreeHGlobal(systemProperties.next);
}
public bool SupportsImageRate() { return m_SupportsImageRate; }
public bool SupportsImageQuality() { return m_SupportsImageQuality; }
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 78bb1f33f93dd034d90b1d1a27a5eb27
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,520 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
namespace VIVE.OpenXR.Passthrough
{
/// <summary>
/// The forms of passthrough layer.
/// </summary>
public enum PassthroughLayerForm
{
///<summary> Fullscreen Passthrough Form</summary>
Planar = 0,
///<summary> Projected Passthrough Form</summary>
Projected = 1
}
/// <summary>
/// The types of passthrough space.
/// </summary>
public enum ProjectedPassthroughSpaceType
{
///<summary>
/// XR_REFERENCE_SPACE_TYPE_VIEW at (0,0,0) with orientation (0,0,0,1)
///</summary>
Headlock = 0,
///<summary>
/// When TrackingOriginMode is TrackingOriginModeFlags.Floor:
/// XR_REFERENCE_SPACE_TYPE_STAGE at (0,0,0) with orientation (0,0,0,1)
///
/// When TrackingOriginMode is TrackingOriginModeFlags.Device:
/// XR_REFERENCE_SPACE_TYPE_LOCAL at (0,0,0) with orientation (0,0,0,1)
///
///</summary>
Worldlock = 1
}
// -------------------- 12.88. XR_HTC_passthrough --------------------
#region New Object Types
/// <summary>
/// An application can create an <see href="https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XrPassthroughHTC">XrPassthroughHTC</see> handle by calling <see href="https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#xrCreatePassthroughHTC">xrCreatePassthroughHTC</see>. The returned passthrough handle can be subsequently used in API calls.
/// </summary>
public struct XrPassthroughHTC : IEquatable<UInt64>
{
private readonly UInt64 value;
public XrPassthroughHTC(UInt64 u)
{
value = u;
}
public static implicit operator UInt64(XrPassthroughHTC equatable)
{
return equatable.value;
}
public static implicit operator XrPassthroughHTC(UInt64 u)
{
return new XrPassthroughHTC(u);
}
public bool Equals(XrPassthroughHTC other)
{
return value == other.value;
}
public bool Equals(UInt64 other)
{
return value == other;
}
public override bool Equals(object obj)
{
return obj is XrPassthroughHTC && Equals((XrPassthroughHTC)obj);
}
public override int GetHashCode()
{
return value.GetHashCode();
}
public override string ToString()
{
return value.ToString();
}
public static bool operator ==(XrPassthroughHTC a, XrPassthroughHTC b) { return a.Equals(b); }
public static bool operator !=(XrPassthroughHTC a, XrPassthroughHTC b) { return !a.Equals(b); }
public static bool operator >=(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value >= b.value; }
public static bool operator <=(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value <= b.value; }
public static bool operator >(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value > b.value; }
public static bool operator <(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value < b.value; }
public static XrPassthroughHTC operator +(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value + b.value; }
public static XrPassthroughHTC operator -(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value - b.value; }
public static XrPassthroughHTC operator *(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value * b.value; }
public static XrPassthroughHTC operator /(XrPassthroughHTC a, XrPassthroughHTC b)
{
if (b.value == 0)
{
throw new DivideByZeroException();
}
return a.value / b.value;
}
}
#endregion
#region New Enums
/// <summary>
/// The XrPassthroughFormHTC enumeration identifies the form of the passthrough, presenting the passthrough fill the full screen or project onto a specified mesh.
/// </summary>
public enum XrPassthroughFormHTC
{
/// <summary>
/// Presents the passthrough with full of the entire screen..
/// </summary>
XR_PASSTHROUGH_FORM_PLANAR_HTC = 0,
/// <summary>
/// Presents the passthrough projecting onto a custom mesh.
/// </summary>
XR_PASSTHROUGH_FORM_PROJECTED_HTC = 1,
};
#endregion
#region New Structures
/// <summary>
/// The XrPassthroughCreateInfoHTC structure describes the information to create an <see cref="XrPassthroughCreateInfoHTC">XrPassthroughCreateInfoHTC</see> handle.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct XrPassthroughCreateInfoHTC
{
/// <summary>
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
/// </summary>
public XrStructureType type;
/// <summary>
/// NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
/// </summary>
public IntPtr next;
/// <summary>
/// The form specifies the form of passthrough.
/// </summary>
public XrPassthroughFormHTC form;
/// <param name="in_type">The <see cref="XrStructureType">XrStructureType</see> of this structure.</param>
/// <param name="in_next">NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.</param>
/// <param name="in_facialTrackingType">An XrFacialTrackingTypeHTC which describes which type of facial tracking should be used for this handle.</param>
public XrPassthroughCreateInfoHTC(XrStructureType in_type, IntPtr in_next, XrPassthroughFormHTC in_form)
{
type = in_type;
next = in_next;
form = in_form;
}
};
/// <summary>
/// The application can specify the XrPassthroughColorHTC to adjust the alpha value of the passthrough. The range is between 0.0f and 1.0f, 1.0f means opaque.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct XrPassthroughColorHTC
{
/// <summary>
/// The XrStructureType of this structure.
/// </summary>
public XrStructureType type;
/// <summary>
/// Next is NULL or a pointer to the next structure in a structure chain, such as XrPassthroughMeshTransformInfoHTC.
/// </summary>
public IntPtr next;
/// <summary>
/// The alpha value of the passthrough in the range [0, 1].
/// </summary>
public float alpha;
public XrPassthroughColorHTC(float in_alpha)
{
type = XrStructureType.XR_TYPE_PASSTHROUGH_COLOR_HTC;
next = IntPtr.Zero;
alpha = in_alpha;
}
};
/// <summary>
/// The XrPassthroughMeshTransformInfoHTC structure describes the mesh and transformation.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct XrPassthroughMeshTransformInfoHTC
{
/// <summary>
/// The XrStructureType of this structure.
/// </summary>
public XrStructureType type;
/// <summary>
/// Next is NULL or a pointer to the next structure in a structure chain.
/// </summary>
public IntPtr next;
/// <summary>
/// The count of vertices array in the mesh.
/// </summary>
public UInt32 vertexCount;
/// <summary>
/// An array of XrVector3f. The size of the array must be equal to vertexCount.
/// </summary>
public IntPtr vertices; // XrVector3f
/// <summary>
/// The count of indices array in the mesh.
/// </summary>
public UInt32 indexCount;
/// <summary>
/// An array of triangle indices. The size of the array must be equal to indexCount.
/// </summary>
public IntPtr indices; // UInt32[]
/// <summary>
/// The XrSpace that defines the projected passthrough's base space for transformations.
/// </summary>
public XrSpace baseSpace;
/// <summary>
/// The XrTime that defines the time at which the transform is applied.
/// </summary>
public XrTime time;
/// <summary>
/// The XrPosef that defines the pose of the mesh
/// </summary>
public XrPosef pose;
/// <summary>
/// The XrVector3f that defines the scale of the mesh
/// </summary>
public XrVector3f scale;
};
/// <summary>
/// A pointer to XrCompositionLayerPassthroughHTC may be submitted in xrEndFrame as a pointer to the base structure XrCompositionLayerBaseHeader, in the desired layer order, to request the runtime to composite a passthrough layer into the final frame output.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct XrCompositionLayerPassthroughHTC
{
/// <summary>
/// The XrStructureType of this structure.
/// </summary>
public XrStructureType type;
/// <summary>
/// Next is NULL or a pointer to the next structure in a structure chain, such as XrPassthroughMeshTransformInfoHTC.
/// </summary>
public IntPtr next;
/// <summary>
/// A bitmask of XrCompositionLayerFlagBits describing flags to apply to the layer.
/// </summary>
public XrCompositionLayerFlags layerFlags;
/// <summary>
/// The XrSpace that specifies the layer¡¦s space - must be XR_NULL_HANDLE.
/// </summary>
public XrSpace space;
/// <summary>
/// The XrPassthroughHTC previously created by xrCreatePassthroughHTC.
/// </summary>
public XrPassthroughHTC passthrough;
/// <summary>
/// The XrPassthroughColorHTC describing the color information with the alpha value of the passthrough layer.
/// </summary>
public XrPassthroughColorHTC color;
public XrCompositionLayerPassthroughHTC(XrCompositionLayerFlags in_layerFlags,
XrSpace in_space, XrPassthroughHTC in_passthrough, XrPassthroughColorHTC in_color)
{
type = XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC;
next = IntPtr.Zero;
layerFlags = in_layerFlags;
space = in_space;
passthrough = in_passthrough;
color = in_color;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct XrPassthroughConfigurationBaseHeaderHTC
{
public XrStructureType type;
public IntPtr next;
};
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct XrPassthroughConfigurationImageRateHTC
{
public XrStructureType type;
public IntPtr next;
public float srcImageRate;
public float dstImageRate;
};
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct XrPassthroughConfigurationImageQualityHTC
{
public XrStructureType type;
public IntPtr next;
public float scale;
};
[StructLayout(LayoutKind.Sequential)]
public struct XrEventDataPassthroughConfigurationImageRateChangedHTC
{
public XrStructureType type;
public IntPtr next;
public XrPassthroughConfigurationImageRateHTC fromImageRate;
public XrPassthroughConfigurationImageRateHTC toImageRate;
public XrEventDataPassthroughConfigurationImageRateChangedHTC(XrStructureType in_type, IntPtr in_next, XrPassthroughConfigurationImageRateHTC in_fromImageRate, XrPassthroughConfigurationImageRateHTC in_toImageRate)
{
type = in_type;
next = in_next;
fromImageRate = in_fromImageRate;
toImageRate = in_toImageRate;
}
public static XrEventDataPassthroughConfigurationImageRateChangedHTC identity
{
get
{
return new XrEventDataPassthroughConfigurationImageRateChangedHTC(
XrStructureType.XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_RATE_CHANGED_HTC,
IntPtr.Zero,
new XrPassthroughConfigurationImageRateHTC { type = XrStructureType.XR_TYPE_PASSTHROUGH_CONFIGURATION_IMAGE_RATE_HTC, next = IntPtr.Zero },
new XrPassthroughConfigurationImageRateHTC { type = XrStructureType.XR_TYPE_PASSTHROUGH_CONFIGURATION_IMAGE_RATE_HTC, next = IntPtr.Zero }); // user is default present
}
}
public static bool Get(XrEventDataBuffer eventDataBuffer, out XrEventDataPassthroughConfigurationImageRateChangedHTC eventDataPassthroughConfigurationImageRate)
{
eventDataPassthroughConfigurationImageRate = identity;
if (eventDataBuffer.type == XrStructureType.XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_RATE_CHANGED_HTC)
{
eventDataPassthroughConfigurationImageRate.next = eventDataBuffer.next;
eventDataPassthroughConfigurationImageRate.fromImageRate.type = (XrStructureType)BitConverter.ToUInt32(eventDataBuffer.varying, 0);
eventDataPassthroughConfigurationImageRate.fromImageRate.next = (IntPtr)BitConverter.ToInt64(eventDataBuffer.varying, 8);
eventDataPassthroughConfigurationImageRate.fromImageRate.srcImageRate = BitConverter.ToSingle(eventDataBuffer.varying, 16);
eventDataPassthroughConfigurationImageRate.fromImageRate.dstImageRate = BitConverter.ToSingle(eventDataBuffer.varying, 20);
eventDataPassthroughConfigurationImageRate.toImageRate.type = (XrStructureType)BitConverter.ToUInt32(eventDataBuffer.varying, 24);
eventDataPassthroughConfigurationImageRate.toImageRate.next = (IntPtr)BitConverter.ToInt64(eventDataBuffer.varying, 32);
eventDataPassthroughConfigurationImageRate.toImageRate.srcImageRate = BitConverter.ToSingle(eventDataBuffer.varying, 40);
eventDataPassthroughConfigurationImageRate.toImageRate.dstImageRate = BitConverter.ToSingle(eventDataBuffer.varying, 44);
return true;
}
return false;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct XrEventDataPassthroughConfigurationImageQualityChangedHTC
{
public XrStructureType type;
public IntPtr next;
public XrPassthroughConfigurationImageQualityHTC fromImageQuality;
public XrPassthroughConfigurationImageQualityHTC toImageQuality;
public XrEventDataPassthroughConfigurationImageQualityChangedHTC(XrStructureType in_type, IntPtr in_next, XrPassthroughConfigurationImageQualityHTC in_fromImageQuality, XrPassthroughConfigurationImageQualityHTC in_toImageQuality)
{
type = in_type;
next = in_next;
fromImageQuality = in_fromImageQuality;
toImageQuality = in_toImageQuality;
}
public static XrEventDataPassthroughConfigurationImageQualityChangedHTC identity
{
get
{
return new XrEventDataPassthroughConfigurationImageQualityChangedHTC(
XrStructureType.XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_QUALITY_CHANGED_HTC,
IntPtr.Zero,
new XrPassthroughConfigurationImageQualityHTC { type = XrStructureType.XR_TYPE_PASSTHROUGH_CONFIGURATION_IMAGE_QUALITY_HTC, next = IntPtr.Zero },
new XrPassthroughConfigurationImageQualityHTC { type = XrStructureType.XR_TYPE_PASSTHROUGH_CONFIGURATION_IMAGE_QUALITY_HTC, next = IntPtr.Zero }); // user is default present
}
}
public static bool Get(XrEventDataBuffer eventDataBuffer, out XrEventDataPassthroughConfigurationImageQualityChangedHTC ventDataPassthroughConfigurationImageQuality)
{
ventDataPassthroughConfigurationImageQuality = identity;
if (eventDataBuffer.type == XrStructureType.XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_QUALITY_CHANGED_HTC)
{
ventDataPassthroughConfigurationImageQuality.next = eventDataBuffer.next;
ventDataPassthroughConfigurationImageQuality.fromImageQuality.type = (XrStructureType)BitConverter.ToUInt32(eventDataBuffer.varying, 0);
ventDataPassthroughConfigurationImageQuality.fromImageQuality.next = (IntPtr)BitConverter.ToInt64(eventDataBuffer.varying, 8);
ventDataPassthroughConfigurationImageQuality.fromImageQuality.scale = BitConverter.ToSingle(eventDataBuffer.varying, 16);
ventDataPassthroughConfigurationImageQuality.toImageQuality.type = (XrStructureType)BitConverter.ToUInt32(eventDataBuffer.varying, 24);
ventDataPassthroughConfigurationImageQuality.toImageQuality.next = (IntPtr)BitConverter.ToInt64(eventDataBuffer.varying, 32);
ventDataPassthroughConfigurationImageQuality.toImageQuality.scale = BitConverter.ToSingle(eventDataBuffer.varying, 40);
return true;
}
return false;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct XrSystemPassthroughConfigurationPropertiesHTC
{
public XrStructureType type;
public IntPtr next;
public XrBool32 supportsImageRate;
public XrBool32 supportsImageQuality;
};
#endregion
#region New Functions
public static class VivePassthroughHelper
{
/// <summary>
/// The delegate function of <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreatePassthroughHTC">xrCreatePassthroughHTC</see>.
/// </summary>
/// <param name="session">An <see cref="XrSession">XrSession</see> in which the passthrough will be active.</param>
/// <param name="createInfo">createInfo is a pointer to an <see cref="XrPassthroughCreateInfoHTC">XrPassthroughCreateInfoHTC</see> structure containing information about how to create the passthrough.</param>
/// <param name="passthrough">passthrough is a pointer to a handle in which the created <see cref="XrPassthroughHTC">XrPassthroughHTC</see> is returned.</param>
/// <returns>XR_SUCCESS for success.</returns>
public delegate XrResult xrCreatePassthroughHTCDelegate(
XrSession session,
XrPassthroughCreateInfoHTC createInfo,
out XrPassthroughHTC passthrough);
/// <summary>
/// The delegate function of <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroyPassthroughHTC">xrDestroyFacialTrackerHTC</see>.
/// </summary>
/// <param name="passthrough">passthrough is the <see cref="XrPassthroughHTC">XrPassthroughHTC</see> to be destroyed..</param>
/// <returns>XR_SUCCESS for success.</returns>
public delegate XrResult xrDestroyPassthroughHTCDelegate(
XrPassthroughHTC passthrough);
public delegate XrResult xrEnumeratePassthroughImageRatesHTCDelegate(
XrSession session,
[In] UInt32 imageRateCapacityInput,
ref UInt32 imageRateCountOutput,
[In, Out] XrPassthroughConfigurationImageRateHTC[] imageRates);
public delegate XrResult xrGetPassthroughConfigurationHTCDelegate(
XrSession session,
IntPtr/*ref XrPassthroughConfigurationBaseHeaderHTC*/ config);
public delegate XrResult xrSetPassthroughConfigurationHTCDelegate(
XrSession session,
IntPtr/*ref XrPassthroughConfigurationBaseHeaderHTC*/ config);
}
public static class VivePassthroughImageQualityChanged
{
public delegate void OnImageQualityChanged(float fromQuality, float toQuality);
public static void Listen(OnImageQualityChanged callback)
{
if (!allEventListeners.Contains(callback))
allEventListeners.Add(callback);
}
public static void Remove(OnImageQualityChanged callback)
{
if (allEventListeners.Contains(callback))
allEventListeners.Remove(callback);
}
public static void Send(float fromQuality, float toQuality)
{
int N = 0;
if (allEventListeners != null)
{
N = allEventListeners.Count;
for (int i = N - 1; i >= 0; i--)
{
OnImageQualityChanged single = allEventListeners[i];
try
{
single(fromQuality, toQuality);
}
catch (Exception e)
{
Debug.Log("Event : " + e.ToString());
allEventListeners.Remove(single);
Debug.Log("Event : A listener is removed due to exception.");
}
}
}
}
private static List<OnImageQualityChanged> allEventListeners = new List<OnImageQualityChanged>();
}
public static class VivePassthroughImageRateChanged
{
public delegate void OnImageRateChanged(float fromSrcImageRate, float fromDestImageRate, float toSrcImageRate, float toDestImageRate);
public static void Listen(OnImageRateChanged callback)
{
if (!allEventListeners.Contains(callback))
allEventListeners.Add(callback);
}
public static void Remove(OnImageRateChanged callback)
{
if (allEventListeners.Contains(callback))
allEventListeners.Remove(callback);
}
public static void Send(float fromSrcImageRate, float fromDestImageRate, float toSrcImageRate, float toDestImageRate)
{
int N = 0;
if (allEventListeners != null)
{
N = allEventListeners.Count;
for (int i = N - 1; i >= 0; i--)
{
OnImageRateChanged single = allEventListeners[i];
try
{
single(fromSrcImageRate, fromDestImageRate, toSrcImageRate, toDestImageRate);
}
catch (Exception e)
{
Debug.Log("Event : " + e.ToString());
allEventListeners.Remove(single);
Debug.Log("Event : A listener is removed due to exception.");
}
}
}
}
private static List<OnImageRateChanged> allEventListeners = new List<OnImageRateChanged>();
}
#endregion
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fa28fb90ea5134443bb348ad98be69bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: