Compare commits
5 Commits
versions/2
...
versions/2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfdcd0fd7f | ||
|
|
5ac252bf2e | ||
|
|
fa1969a087 | ||
|
|
3dd72f5f56 | ||
|
|
7f2a459592 |
4
LICENSE.txt
Normal file
4
LICENSE.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Copyright © HTC Corporation, LLC and its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
Your use of this SDK, sample, or tool is subject to HTC VIVE SDK License Agreement, available at https://developer.vive.com/resources/downloads/licenses-and-agreements/
|
||||||
|
|
||||||
@@ -67,6 +67,18 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
static GUIContent Label_AngleOfArc = new GUIContent("Arc Angle", "Central angle of arc of Cylinder Layer");
|
static GUIContent Label_AngleOfArc = new GUIContent("Arc Angle", "Central angle of arc of Cylinder Layer");
|
||||||
SerializedProperty Property_AngleOfArc;
|
SerializedProperty Property_AngleOfArc;
|
||||||
|
|
||||||
|
static string PropertyName_isExternalSurface = "isExternalSurface";
|
||||||
|
static GUIContent Label_isExternalSurface = new GUIContent("External Surface", "Specify using external surface or not.");
|
||||||
|
SerializedProperty Property_isExternalSurface;
|
||||||
|
|
||||||
|
static string PropertyName_ExternalSurfaceWidth = "externalSurfaceWidth";
|
||||||
|
static GUIContent Label_ExternalSurfaceWidth = new GUIContent("Width");
|
||||||
|
SerializedProperty Property_ExternalSurfaceWidth;
|
||||||
|
|
||||||
|
static string PropertyName_ExternalSurfaceHeight = "externalSurfaceHeight";
|
||||||
|
static GUIContent Label_ExternalSurfaceHeight = new GUIContent("Height");
|
||||||
|
SerializedProperty Property_ExternalSurfaceHeight;
|
||||||
|
|
||||||
static string PropertyName_IsDynamicLayer = "isDynamicLayer";
|
static string PropertyName_IsDynamicLayer = "isDynamicLayer";
|
||||||
static GUIContent Label_IsDynamicLayer = new GUIContent("Dynamic Layer", "Specify whether Layer needs to be updated each frame or not.");
|
static GUIContent Label_IsDynamicLayer = new GUIContent("Dynamic Layer", "Specify whether Layer needs to be updated each frame or not.");
|
||||||
SerializedProperty Property_IsDynamicLayer;
|
SerializedProperty Property_IsDynamicLayer;
|
||||||
@@ -75,6 +87,11 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
static GUIContent Label_ApplyColorScaleBias = new GUIContent("Apply Color Scale Bias", "Color scale and bias are applied to a layer color during composition, after its conversion to premultiplied alpha representation. LayerColor = LayerColor * colorScale + colorBias");
|
static GUIContent Label_ApplyColorScaleBias = new GUIContent("Apply Color Scale Bias", "Color scale and bias are applied to a layer color during composition, after its conversion to premultiplied alpha representation. LayerColor = LayerColor * colorScale + colorBias");
|
||||||
SerializedProperty Property_ApplyColorScaleBias;
|
SerializedProperty Property_ApplyColorScaleBias;
|
||||||
|
|
||||||
|
static string PropertyName_SolidEffect = "solidEffect";
|
||||||
|
static GUIContent Label_SolidEffect = new GUIContent("Solid Effect", "Apply UnderLay Color Scale Bias in Runtime Level.");
|
||||||
|
SerializedProperty Property_SolidEffect;
|
||||||
|
|
||||||
|
|
||||||
static string PropertyName_ColorScale = "colorScale";
|
static string PropertyName_ColorScale = "colorScale";
|
||||||
static GUIContent Label_ColorScale = new GUIContent("Color Scale", "Will be used for modulatting the color sourced from the images.");
|
static GUIContent Label_ColorScale = new GUIContent("Color Scale", "Will be used for modulatting the color sourced from the images.");
|
||||||
SerializedProperty Property_ColorScale;
|
SerializedProperty Property_ColorScale;
|
||||||
@@ -96,9 +113,10 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
SerializedProperty Property_TrackingOrigin;
|
SerializedProperty Property_TrackingOrigin;
|
||||||
|
|
||||||
private bool showLayerParams = true, showColorScaleBiasParams = true;
|
private bool showLayerParams = true, showColorScaleBiasParams = true;
|
||||||
#pragma warning disable
|
|
||||||
private bool showExternalSurfaceParams = false;
|
private bool showExternalSurfaceParams = false;
|
||||||
#pragma warning restore
|
|
||||||
|
|
||||||
public override void OnInspectorGUI()
|
public override void OnInspectorGUI()
|
||||||
{
|
{
|
||||||
if (Property_LayerType == null) Property_LayerType = serializedObject.FindProperty(PropertyName_LayerType);
|
if (Property_LayerType == null) Property_LayerType = serializedObject.FindProperty(PropertyName_LayerType);
|
||||||
@@ -112,8 +130,12 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
if (Property_CylinderArcLength == null) Property_CylinderArcLength = serializedObject.FindProperty(PropertyName_CylinderArcLength);
|
if (Property_CylinderArcLength == null) Property_CylinderArcLength = serializedObject.FindProperty(PropertyName_CylinderArcLength);
|
||||||
if (Property_CylinderRadius == null) Property_CylinderRadius = serializedObject.FindProperty(PropertyName_CylinderRadius);
|
if (Property_CylinderRadius == null) Property_CylinderRadius = serializedObject.FindProperty(PropertyName_CylinderRadius);
|
||||||
if (Property_AngleOfArc == null) Property_AngleOfArc = serializedObject.FindProperty(PropertyName_AngleOfArc);
|
if (Property_AngleOfArc == null) Property_AngleOfArc = serializedObject.FindProperty(PropertyName_AngleOfArc);
|
||||||
|
if (Property_isExternalSurface == null) Property_isExternalSurface = serializedObject.FindProperty(PropertyName_isExternalSurface);
|
||||||
|
if (Property_ExternalSurfaceWidth == null) Property_ExternalSurfaceWidth = serializedObject.FindProperty(PropertyName_ExternalSurfaceWidth);
|
||||||
|
if (Property_ExternalSurfaceHeight == null) Property_ExternalSurfaceHeight = serializedObject.FindProperty(PropertyName_ExternalSurfaceHeight);
|
||||||
if (Property_IsDynamicLayer == null) Property_IsDynamicLayer = serializedObject.FindProperty(PropertyName_IsDynamicLayer);
|
if (Property_IsDynamicLayer == null) Property_IsDynamicLayer = serializedObject.FindProperty(PropertyName_IsDynamicLayer);
|
||||||
if (Property_ApplyColorScaleBias == null) Property_ApplyColorScaleBias = serializedObject.FindProperty(PropertyName_ApplyColorScaleBias);
|
if (Property_ApplyColorScaleBias == null) Property_ApplyColorScaleBias = serializedObject.FindProperty(PropertyName_ApplyColorScaleBias);
|
||||||
|
if (Property_SolidEffect == null) Property_SolidEffect = serializedObject.FindProperty(PropertyName_SolidEffect);
|
||||||
if (Property_ColorScale == null) Property_ColorScale = serializedObject.FindProperty(PropertyName_ColorScale);
|
if (Property_ColorScale == null) Property_ColorScale = serializedObject.FindProperty(PropertyName_ColorScale);
|
||||||
if (Property_ColorBias == null) Property_ColorBias = serializedObject.FindProperty(PropertyName_ColorBias);
|
if (Property_ColorBias == null) Property_ColorBias = serializedObject.FindProperty(PropertyName_ColorBias);
|
||||||
if (Property_IsProtectedSurface == null) Property_IsProtectedSurface = serializedObject.FindProperty(PropertyName_IsProtectedSurface);
|
if (Property_IsProtectedSurface == null) Property_IsProtectedSurface = serializedObject.FindProperty(PropertyName_IsProtectedSurface);
|
||||||
@@ -430,6 +452,25 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
EditorGUILayout.PropertyField(Property_IsDynamicLayer, Label_IsDynamicLayer);
|
EditorGUILayout.PropertyField(Property_IsDynamicLayer, Label_IsDynamicLayer);
|
||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
//EditorGUILayout.PropertyField(Property_isExternalSurface, Label_isExternalSurface);
|
||||||
|
//serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
//if (targetCompositionLayer.isExternalSurface)
|
||||||
|
if (false)
|
||||||
|
{
|
||||||
|
EditorGUI.indentLevel++;
|
||||||
|
showExternalSurfaceParams = EditorGUILayout.Foldout(showExternalSurfaceParams, "External Surface Parameters");
|
||||||
|
if (showExternalSurfaceParams)
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(Property_ExternalSurfaceWidth, Label_ExternalSurfaceWidth);
|
||||||
|
EditorGUILayout.PropertyField(Property_ExternalSurfaceHeight, Label_ExternalSurfaceHeight);
|
||||||
|
EditorGUILayout.PropertyField(Property_IsProtectedSurface, Label_IsProtectedSurface);
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
EditorGUI.indentLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
EditorGUILayout.PropertyField(Property_ApplyColorScaleBias, Label_ApplyColorScaleBias);
|
EditorGUILayout.PropertyField(Property_ApplyColorScaleBias, Label_ApplyColorScaleBias);
|
||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
@@ -441,6 +482,11 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
EditorGUI.indentLevel++;
|
EditorGUI.indentLevel++;
|
||||||
|
if (targetCompositionLayer.layerType == CompositionLayer.LayerType.Underlay)
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(Property_SolidEffect, Label_SolidEffect);
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
showColorScaleBiasParams = EditorGUILayout.Foldout(showColorScaleBiasParams, "Color Scale Bias Parameters");
|
showColorScaleBiasParams = EditorGUILayout.Foldout(showColorScaleBiasParams, "Color Scale Bias Parameters");
|
||||||
if (showColorScaleBiasParams)
|
if (showColorScaleBiasParams)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,24 +7,26 @@ using UnityEditor.XR.OpenXR.Features;
|
|||||||
namespace VIVE.OpenXR
|
namespace VIVE.OpenXR
|
||||||
{
|
{
|
||||||
[OpenXRFeatureSet(
|
[OpenXRFeatureSet(
|
||||||
FeatureIds = new string[] {
|
FeatureIds = new string[] {
|
||||||
VIVEFocus3Feature.featureId,
|
VIVEFocus3Feature.featureId,
|
||||||
VIVEFocus3Profile.featureId,
|
VIVEFocus3Profile.featureId,
|
||||||
Hand.ViveHandTracking.featureId,
|
Hand.ViveHandTracking.featureId,
|
||||||
"vive.vive.openxr.feature.compositionlayer",
|
"vive.openxr.feature.compositionlayer",
|
||||||
"vive.vive.openxr.feature.compositionlayer.cylinder",
|
"vive.openxr.feature.compositionlayer.cylinder",
|
||||||
"vive.vive.openxr.feature.compositionlayer.colorscalebias",
|
"vive.openxr.feature.compositionlayer.colorscalebias",
|
||||||
Tracker.ViveWristTracker.featureId,
|
Tracker.ViveWristTracker.featureId,
|
||||||
Hand.ViveHandInteraction.featureId,
|
Hand.ViveHandInteraction.featureId,
|
||||||
"vive.vive.openxr.feature.foveation",
|
"vive.openxr.feature.foveation",
|
||||||
FacialTracking.ViveFacialTracking.featureId,
|
FacialTracking.ViveFacialTracking.featureId,
|
||||||
},
|
PlaneDetection.VivePlaneDetection.featureId,
|
||||||
UiName = "VIVE XR Support",
|
Anchor.ViveAnchor.featureId,
|
||||||
Description = "Necessary to deploy an VIVE XR compatible app.",
|
},
|
||||||
FeatureSetId = "com.htc.vive.openxr.featureset.vivexr",
|
UiName = "VIVE XR Support",
|
||||||
DefaultFeatureIds = new string[] { VIVEFocus3Feature.featureId, VIVEFocus3Profile.featureId, },
|
Description = "Necessary to deploy an VIVE XR compatible app.",
|
||||||
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.Android }
|
FeatureSetId = "com.htc.vive.openxr.featureset.vivexr",
|
||||||
)]
|
DefaultFeatureIds = new string[] { VIVEFocus3Feature.featureId, VIVEFocus3Profile.featureId, },
|
||||||
|
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.Android }
|
||||||
|
)]
|
||||||
sealed class VIVEFocus3FeatureSet { }
|
sealed class VIVEFocus3FeatureSet { }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 28cd706d4fe07ba4c85df80c80bbdee6
|
guid: d55a23fb1f9c7b0479d1f15716620072
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.Feature
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// To use this wrapper, you need to call CommonWrapper.Instance.OnInstanceCreate() in your feature's OnInstanceCreate(),
|
||||||
|
/// and call CommonWrapper.Instance.OnInstanceDestroy() in your feature's OnInstanceDestroy().
|
||||||
|
/// </summary>
|
||||||
|
public class CommonWrapper
|
||||||
|
{
|
||||||
|
static CommonWrapper instance = null;
|
||||||
|
public static CommonWrapper Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
instance = new CommonWrapper();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInited = false;
|
||||||
|
|
||||||
|
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||||
|
OpenXRHelper.xrGetSystemPropertiesDelegate XrGetSystemProperties;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// In feature's OnInstanceCreate(), call CommonWrapper.Instance.OnInstanceCreate() for init common APIs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xrInstance">Passed in feature's OnInstanceCreate.</param>
|
||||||
|
/// <param name="xrGetInstanceProcAddr">Pass OpenXRFeature.xrGetInstanceProcAddr in.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="Exception">If input data not valid.</exception>
|
||||||
|
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr xrGetInstanceProcAddr)
|
||||||
|
{
|
||||||
|
if (isInited) return true;
|
||||||
|
|
||||||
|
if (xrInstance == 0)
|
||||||
|
throw new Exception("CommonWrapper: xrInstance is null");
|
||||||
|
|
||||||
|
Debug.Log("CommonWrapper: OnInstanceCreate()");
|
||||||
|
/// OpenXRFeature.xrGetInstanceProcAddr
|
||||||
|
if (xrGetInstanceProcAddr == null || xrGetInstanceProcAddr == IntPtr.Zero)
|
||||||
|
throw new Exception("CommonWrapper: xrGetInstanceProcAddr is null");
|
||||||
|
|
||||||
|
Debug.Log("CommonWrapper: Get function pointer of xrGetInstanceProcAddr.");
|
||||||
|
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
xrGetInstanceProcAddr,
|
||||||
|
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
|
||||||
|
|
||||||
|
bool ret = true;
|
||||||
|
IntPtr funcPtr = IntPtr.Zero;
|
||||||
|
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(XrGetInstanceProcAddr, xrInstance, "xrGetSystemProperties", out XrGetSystemProperties);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
throw new Exception("CommonWrapper: Get function pointers failed.");
|
||||||
|
|
||||||
|
isInited = ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// In feature's OnInstanceDestroy(), call CommonWrapper.Instance.OnInstanceDestroy() for disable common APIs.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public void OnInstanceDestroy()
|
||||||
|
{
|
||||||
|
isInited = false;
|
||||||
|
XrGetInstanceProcAddr = null;
|
||||||
|
XrGetSystemProperties = null;
|
||||||
|
Debug.Log("CommonWrapper: OnInstanceDestroy()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public XrResult GetInstanceProcAddr(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
if (isInited == false || XrGetInstanceProcAddr == null)
|
||||||
|
{
|
||||||
|
function = IntPtr.Zero;
|
||||||
|
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return XrGetInstanceProcAddr(instance, name, out function);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper function to get system properties. Need input your features' xrInstance and xrSystemId. Fill the system properites in next for you feature.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instance"></param>
|
||||||
|
/// <param name="systemId"></param>
|
||||||
|
/// <param name="properties"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrResult GetSystemProperties(XrInstance instance, XrSystemId systemId, ref XrSystemProperties properties)
|
||||||
|
{
|
||||||
|
if (isInited == false || XrGetSystemProperties == null)
|
||||||
|
{
|
||||||
|
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return XrGetSystemProperties(instance, systemId, ref properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public XrResult GetProperties<T>(XrInstance instance, XrSystemId systemId, ref T featureProperty)
|
||||||
|
{
|
||||||
|
XrSystemProperties systemProperties = new XrSystemProperties();
|
||||||
|
systemProperties.type = XrStructureType.XR_TYPE_SYSTEM_PROPERTIES;
|
||||||
|
systemProperties.next = Marshal.AllocHGlobal(Marshal.SizeOf(featureProperty));
|
||||||
|
|
||||||
|
long offset = 0;
|
||||||
|
if (IntPtr.Size == 4)
|
||||||
|
offset = systemProperties.next.ToInt32();
|
||||||
|
else
|
||||||
|
offset = systemProperties.next.ToInt64();
|
||||||
|
|
||||||
|
IntPtr pdPropertiesPtr = new IntPtr(offset);
|
||||||
|
Marshal.StructureToPtr(featureProperty, pdPropertiesPtr, false);
|
||||||
|
|
||||||
|
var ret = GetSystemProperties(instance, systemId, ref systemProperties);
|
||||||
|
if (ret == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (IntPtr.Size == 4)
|
||||||
|
offset = systemProperties.next.ToInt32();
|
||||||
|
else
|
||||||
|
offset = systemProperties.next.ToInt64();
|
||||||
|
|
||||||
|
pdPropertiesPtr = new IntPtr(offset);
|
||||||
|
featureProperty = Marshal.PtrToStructure<T>(pdPropertiesPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Marshal.FreeHGlobal(systemProperties.next);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 93517fb9198ee33428984693d60b4061
|
guid: b940cd65f52cd5c44bd79869c5d521b2
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
// Remove FAKE_DATA if editor or windows is supported.
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
#define FAKE_DATA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.Feature
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// To use this wrapper, you need to call CommonWrapper.Instance.OnInstanceCreate() in your feature's OnInstanceCreate(),
|
||||||
|
/// and call CommonWrapper.Instance.OnInstanceDestroy() in your feature's OnInstanceDestroy().
|
||||||
|
/// </summary>
|
||||||
|
public class SpaceWrapper
|
||||||
|
{
|
||||||
|
static SpaceWrapper instance = null;
|
||||||
|
public static SpaceWrapper Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
instance = new SpaceWrapper();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInited = false;
|
||||||
|
|
||||||
|
delegate XrResult DelegateXrLocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location);
|
||||||
|
delegate XrResult DelegateXrDestroySpace(XrSpace space);
|
||||||
|
|
||||||
|
OpenXRHelper.xrCreateReferenceSpaceDelegate XrCreateReferenceSpace;
|
||||||
|
DelegateXrLocateSpace XrLocateSpace;
|
||||||
|
DelegateXrDestroySpace XrDestroySpace;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Features should call ViveSpaceWrapper.Instance.OnInstanceCreate() in their OnInstanceCreate().
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xrInstance"></param>
|
||||||
|
/// <param name="GetAddr"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public bool OnInstanceCreate(XrInstance xrInstance, OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr)
|
||||||
|
{
|
||||||
|
if (isInited) return true;
|
||||||
|
|
||||||
|
if (xrInstance == null)
|
||||||
|
throw new Exception("ViveSpace: xrInstance is null");
|
||||||
|
|
||||||
|
if (GetAddr == null)
|
||||||
|
throw new Exception("ViveSpace: xrGetInstanceProcAddr is null");
|
||||||
|
|
||||||
|
Debug.Log("ViveSpace: OnInstanceCreate()");
|
||||||
|
|
||||||
|
bool ret = true;
|
||||||
|
IntPtr funcPtr = IntPtr.Zero;
|
||||||
|
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrCreateReferenceSpace", out XrCreateReferenceSpace);
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrLocateSpace", out XrLocateSpace);
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrDestroySpace", out XrDestroySpace);
|
||||||
|
isInited = ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnInstanceDestroy()
|
||||||
|
{
|
||||||
|
isInited = false;
|
||||||
|
XrCreateReferenceSpace = null;
|
||||||
|
XrLocateSpace = null;
|
||||||
|
XrDestroySpace = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a reference space without create info.
|
||||||
|
/// Example:
|
||||||
|
/// CreateReferenceSpace(session, XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_LOCAL, XrPosef.identity, out space);
|
||||||
|
/// CreateReferenceSpace(session, XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_STAGE, XrPosef.identity, out space);
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="session"></param>
|
||||||
|
/// <param name="referenceSpaceType"></param>
|
||||||
|
/// <param name="pose"></param>
|
||||||
|
/// <param name="space"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrResult CreateReferenceSpace(XrSession session, XrReferenceSpaceType referenceSpaceType, XrPosef pose, out XrSpace space)
|
||||||
|
{
|
||||||
|
if (!isInited)
|
||||||
|
throw new Exception("ViveSpace: not initialized");
|
||||||
|
|
||||||
|
var createInfo = new XrReferenceSpaceCreateInfo();
|
||||||
|
createInfo.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
||||||
|
createInfo.next = IntPtr.Zero;
|
||||||
|
createInfo.referenceSpaceType = referenceSpaceType;
|
||||||
|
createInfo.poseInReferenceSpace = pose;
|
||||||
|
return XrCreateReferenceSpace(session, ref createInfo, out space);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a reference space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="session"></param>
|
||||||
|
/// <param name="createInfo"></param>
|
||||||
|
/// <param name="space"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrResult CreateReferenceSpace(XrSession session, XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
||||||
|
{
|
||||||
|
if (!isInited)
|
||||||
|
throw new Exception("ViveSpace: not initialized");
|
||||||
|
|
||||||
|
return XrCreateReferenceSpace(session, ref createInfo, out space);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XrResult LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location)
|
||||||
|
{
|
||||||
|
if (!isInited)
|
||||||
|
throw new Exception("ViveSpace: not initialized");
|
||||||
|
Debug.Log($"LocateSpace(s={space}, bs={baseSpace}, t={time}");
|
||||||
|
return XrLocateSpace(space, baseSpace, time, ref location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XrResult DestroySpace(XrSpace space)
|
||||||
|
{
|
||||||
|
if (!isInited)
|
||||||
|
throw new Exception("ViveSpace: not initialized");
|
||||||
|
Debug.Log($"DestroySpace({space})");
|
||||||
|
return XrDestroySpace(space);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrSpace's Unity wrapper. Input and output are in Unity coordinate system.
|
||||||
|
/// After use it, you should call Dispose() to release the XrSpace.
|
||||||
|
/// </summary>
|
||||||
|
public class Space : IDisposable
|
||||||
|
{
|
||||||
|
protected XrSpace space;
|
||||||
|
private bool disposed = false;
|
||||||
|
|
||||||
|
public Space(XrSpace space)
|
||||||
|
{
|
||||||
|
Debug.Log($"Space({space})");
|
||||||
|
this.space = space;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the raw XrSpace. Only use it when class Space instance is alive.
|
||||||
|
/// You should not try to store this XrSpace, because it may be destroyed.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrSpace GetXrSpace()
|
||||||
|
{
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetRelatedPose(XrSpace baseSpace, XrTime time, out UnityEngine.Pose pose)
|
||||||
|
{
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
{
|
||||||
|
// make a random Pose
|
||||||
|
//var pos = new Vector3(UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f));
|
||||||
|
//var rot = new Quaternion(UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f));
|
||||||
|
var pos = Vector3.up;
|
||||||
|
var rot = Quaternion.identity;
|
||||||
|
rot.Normalize();
|
||||||
|
pose = new Pose(pos, rot);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// If the xrBaseSpace is changed, the pose will be updated.
|
||||||
|
pose = default;
|
||||||
|
XrSpaceLocation location = new XrSpaceLocation();
|
||||||
|
location.type = XrStructureType.XR_TYPE_SPACE_LOCATION;
|
||||||
|
location.next = IntPtr.Zero;
|
||||||
|
var ret = SpaceWrapper.Instance.LocateSpace(space, baseSpace, time, ref location);
|
||||||
|
|
||||||
|
if (ret != XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
Debug.Log("Space: LocateSpace ret=" + ret);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("Space: baseSpace=" + baseSpace + ", space=" + space + ", time=" + time + ", ret=" + ret);
|
||||||
|
Debug.Log("Space: location.locationFlags=" + location.locationFlags);
|
||||||
|
Debug.Log("Space: location.pose.position=" + location.pose.position.x + "," + location.pose.position.y + "," + location.pose.position.z);
|
||||||
|
Debug.Log("Space: location.pose.orientation=" + location.pose.orientation.x + "," + location.pose.orientation.y + "," + location.pose.orientation.z + "," + location.pose.orientation.w);
|
||||||
|
if ((location.locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_POSITION_VALID_BIT) > 0 &&
|
||||||
|
(location.locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) > 0)
|
||||||
|
{
|
||||||
|
pose = new Pose(location.pose.position.ToUnityVector(), location.pose.orientation.ToUnityQuaternion());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// Managered resource
|
||||||
|
}
|
||||||
|
// Non managered resource
|
||||||
|
Debug.Log($"Space: DestroySpace({space})");
|
||||||
|
SpaceWrapper.Instance.DestroySpace(space);
|
||||||
|
space = 0;
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~Space()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: f0160138780388c4aaa979212523b31f
|
guid: e8fbeadd31afbe14996c061ac261041d
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
83
com.htc.upm.vive.openxr/Runtime/Common/ViveInterceptors.cs
Normal file
83
com.htc.upm.vive.openxr/Runtime/Common/ViveInterceptors.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using AOT;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class is made for all features that need to intercept OpenXR API calls.
|
||||||
|
/// Some APIs will be called by Unity internally, and we need to intercept them in c# to get some information.
|
||||||
|
/// Append more interceptable functions for this class by adding a new partial class.
|
||||||
|
/// The partial class can help the delegate name be nice to read and search.
|
||||||
|
/// Please create per function in one partial class.
|
||||||
|
///
|
||||||
|
/// For all features want to use this class, please call <see cref="HookGetInstanceProcAddr" /> in your feature class.
|
||||||
|
/// For example:
|
||||||
|
/// protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
|
/// {
|
||||||
|
/// return HtcInterceptors.Instance.HookGetInstanceProcAddr(func);
|
||||||
|
/// }
|
||||||
|
/// </summary>
|
||||||
|
partial class ViveInterceptors
|
||||||
|
{
|
||||||
|
public const string TAG = "Interceptors";
|
||||||
|
|
||||||
|
public static ViveInterceptors instance = null;
|
||||||
|
public static ViveInterceptors Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
instance = new ViveInterceptors();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ViveInterceptors()
|
||||||
|
{
|
||||||
|
Debug.Log("HtcInterceptors");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInited = false;
|
||||||
|
|
||||||
|
public delegate XrResult DelegateXrGetInstanceProcAddr(XrInstance instance, string name, out IntPtr function);
|
||||||
|
private static readonly DelegateXrGetInstanceProcAddr hookXrGetInstanceProcAddrHandle = new DelegateXrGetInstanceProcAddr(XrGetInstanceProcAddrInterceptor);
|
||||||
|
private static readonly IntPtr hookGetInstanceProcAddrHandlePtr = Marshal.GetFunctionPointerForDelegate(hookXrGetInstanceProcAddrHandle);
|
||||||
|
static DelegateXrGetInstanceProcAddr XrGetInstanceProcAddrOriginal = null;
|
||||||
|
|
||||||
|
[MonoPInvokeCallback(typeof(DelegateXrGetInstanceProcAddr))]
|
||||||
|
private static XrResult XrGetInstanceProcAddrInterceptor(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
// Custom interceptors
|
||||||
|
if (name == "xrWaitFrame")
|
||||||
|
{
|
||||||
|
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
|
||||||
|
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||||
|
if (ret == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
XrWaitFrameOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrWaitFrame>(function);
|
||||||
|
function = xrWaitFrameInterceptorPtr;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
|
{
|
||||||
|
Debug.Log($"{TAG}: registering our own xrGetInstanceProcAddr");
|
||||||
|
if (XrGetInstanceProcAddrOriginal == null)
|
||||||
|
{
|
||||||
|
XrGetInstanceProcAddrOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrGetInstanceProcAddr>(func);
|
||||||
|
isInited = true;
|
||||||
|
return hookGetInstanceProcAddrHandlePtr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: be2b7b0a80ee6ac498ac4ff6bc764cf6
|
guid: 118a2474c266d174d834b364821865b5
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using AOT;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
partial class ViveInterceptors
|
||||||
|
{
|
||||||
|
#region XRWaitFrame
|
||||||
|
public struct XrFrameWaitInfo
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct XrFrameState
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public XrTime predictedDisplayTime;
|
||||||
|
public XrDuration predictedDisplayPeriod;
|
||||||
|
public XrBool32 shouldRender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate XrResult DelegateXrWaitFrame(XrSession session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState);
|
||||||
|
private static readonly DelegateXrWaitFrame xrWaitFrameInterceptorHandle = new DelegateXrWaitFrame(XrWaitFrameInterceptor);
|
||||||
|
private static readonly IntPtr xrWaitFrameInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrWaitFrameInterceptorHandle);
|
||||||
|
static DelegateXrWaitFrame XrWaitFrameOriginal = null;
|
||||||
|
|
||||||
|
[MonoPInvokeCallback(typeof(DelegateXrWaitFrame))]
|
||||||
|
private static XrResult XrWaitFrameInterceptor(XrSession session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState)
|
||||||
|
{
|
||||||
|
var ret = XrWaitFrameOriginal(session, ref frameWaitInfo, ref frameState);
|
||||||
|
currentFrameState = frameState;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static XrFrameState currentFrameState = new XrFrameState() { predictedDisplayTime = 0 };
|
||||||
|
|
||||||
|
public XrFrameState GetCurrentFrameState()
|
||||||
|
{
|
||||||
|
if (!isInited) throw new Exception("ViveInterceptors is not inited");
|
||||||
|
|
||||||
|
return currentFrameState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XrTime GetPredictTime()
|
||||||
|
{
|
||||||
|
if (!isInited) throw new Exception("ViveInterceptors is not inited");
|
||||||
|
|
||||||
|
Debug.Log($"{TAG}: XrWaitFrameInterceptor(predictedDisplayTime={currentFrameState.predictedDisplayTime}");
|
||||||
|
if (currentFrameState.predictedDisplayTime == 0)
|
||||||
|
return new XrTime((long)(1000000L * (Time.unscaledTimeAsDouble + 0.011f)));
|
||||||
|
else
|
||||||
|
return currentFrameState.predictedDisplayTime;
|
||||||
|
}
|
||||||
|
#endregion XRWaitFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: fbe8b74550a44c54fabdef905e9b1678
|
guid: 9bfc07f267ee39b47a4bcbb8c1c786cb
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// "VIVE SDK
|
||||||
|
// © 2020 HTC Corporation. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Unless otherwise required by copyright law and practice,
|
||||||
|
// upon the execution of HTC SDK license agreement,
|
||||||
|
// HTC grants you access to and use of the VIVE SDK(s).
|
||||||
|
// You shall fully comply with all of HTC’s SDK license agreement terms and
|
||||||
|
// conditions signed by you and all SDK and API requirements,
|
||||||
|
// specifications, and documentation provided by HTC to You."
|
||||||
|
|
||||||
|
Shader "VIVE/OpenXR/CompositionLayer/UnderlayAlphaZeroSolid"
|
||||||
|
{
|
||||||
|
Properties
|
||||||
|
{
|
||||||
|
_MainTex("Texture", 2D) = "white" {}
|
||||||
|
_ColorScale("Color Scale", Color) = (1,1,1,1)
|
||||||
|
_ColorBias("Color Bias", Color) = (0,0,0,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
SubShader
|
||||||
|
{
|
||||||
|
Tags {"Queue" = "Transparent" "RenderType" = "Transparent"}
|
||||||
|
LOD 500
|
||||||
|
|
||||||
|
ZWrite On
|
||||||
|
Blend Zero Zero
|
||||||
|
|
||||||
|
Pass
|
||||||
|
{
|
||||||
|
CGPROGRAM
|
||||||
|
#pragma vertex vert
|
||||||
|
#pragma fragment frag
|
||||||
|
|
||||||
|
#pragma multi_compile_local _ COLOR_SCALE_BIAS_ENABLED
|
||||||
|
|
||||||
|
#include "UnityCG.cginc"
|
||||||
|
|
||||||
|
struct appdata
|
||||||
|
{
|
||||||
|
float4 vertex : POSITION;
|
||||||
|
float2 uv : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct v2f
|
||||||
|
{
|
||||||
|
float2 uv : TEXCOORD0;
|
||||||
|
float4 vertex : SV_POSITION;
|
||||||
|
};
|
||||||
|
|
||||||
|
sampler2D _MainTex;
|
||||||
|
float4 _MainTex_ST;
|
||||||
|
|
||||||
|
fixed4 _ColorScale;
|
||||||
|
fixed4 _ColorBias;
|
||||||
|
|
||||||
|
v2f vert(appdata v)
|
||||||
|
{
|
||||||
|
v2f o;
|
||||||
|
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||||
|
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed4 frag(v2f i) : SV_Target
|
||||||
|
{
|
||||||
|
fixed4 col = tex2D(_MainTex, i.uv);
|
||||||
|
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
ENDCG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 33a39a24d09f3cf48ae13735ee2209f6
|
||||||
|
ShaderImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
defaultTextures: []
|
||||||
|
nonModifiableTextures: []
|
||||||
|
preprocessorOverride: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -96,9 +96,23 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
[SerializeField]
|
[SerializeField]
|
||||||
public bool isDynamicLayer = false;
|
public bool isDynamicLayer = false;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
public bool isExternalSurface = false;
|
||||||
|
|
||||||
|
[Tooltip("Width of external surface in pixels.")]
|
||||||
|
[SerializeField]
|
||||||
|
public uint externalSurfaceWidth = 1280;
|
||||||
|
|
||||||
|
[Tooltip("Height of external surface in pixels.")]
|
||||||
|
[SerializeField]
|
||||||
|
public uint externalSurfaceHeight = 720;
|
||||||
|
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
public bool applyColorScaleBias = false;
|
public bool applyColorScaleBias = false;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
public bool solidEffect = false;
|
||||||
|
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
public Color colorScale = Color.white;
|
public Color colorScale = Color.white;
|
||||||
|
|
||||||
@@ -184,6 +198,58 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isExternalSurface)
|
||||||
|
{
|
||||||
|
CompositionLayerRenderThreadSyncObject SetupExternalAndroidSurfaceSyncObjects = new CompositionLayerRenderThreadSyncObject(
|
||||||
|
(taskQueue) =>
|
||||||
|
{
|
||||||
|
lock (taskQueue)
|
||||||
|
{
|
||||||
|
CompositionLayerRenderThreadTask task = (CompositionLayerRenderThreadTask)taskQueue.Dequeue();
|
||||||
|
|
||||||
|
//Enumerate Swapchain formats
|
||||||
|
compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>();
|
||||||
|
|
||||||
|
uint imageCount;
|
||||||
|
|
||||||
|
GraphicsAPI graphicsAPI = GraphicsAPI.GLES3;
|
||||||
|
|
||||||
|
switch (SystemInfo.graphicsDeviceType)
|
||||||
|
{
|
||||||
|
case UnityEngine.Rendering.GraphicsDeviceType.OpenGLES3:
|
||||||
|
graphicsAPI = GraphicsAPI.GLES3;
|
||||||
|
break;
|
||||||
|
case UnityEngine.Rendering.GraphicsDeviceType.Vulkan:
|
||||||
|
graphicsAPI = GraphicsAPI.Vulkan;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ERROR("Unsupported Graphics API, aborting init process.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
layerID = compositionLayerFeature.CompositionLayer_Init(externalSurfaceWidth, externalSurfaceHeight, graphicsAPI, isDynamicLayer, isProtectedSurface, out imageCount, true);
|
||||||
|
|
||||||
|
if (layerID != 0)
|
||||||
|
{
|
||||||
|
DEBUG("Init completed, ID: " + layerID);
|
||||||
|
layerTextures = new LayerTextures(imageCount);
|
||||||
|
InitStatus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
taskQueue.Release(task);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CompositionLayerRenderThreadTask.IssueObtainSwapchainEvent(SetupExternalAndroidSurfaceSyncObjects);
|
||||||
|
|
||||||
|
texture = new Texture2D((int)externalSurfaceWidth, (int)externalSurfaceHeight, TextureFormat.RGBA32, false, isLinear);
|
||||||
|
|
||||||
|
|
||||||
|
DEBUG("CompositionLayerInit Ext Surf");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
{
|
{
|
||||||
ERROR("CompositionLayerInit: Source Texture not found, abort init.");
|
ERROR("CompositionLayerInit: Source Texture not found, abort init.");
|
||||||
@@ -259,6 +325,22 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
{
|
{
|
||||||
if (!isInitializationComplete || !isSynchronized) return false;
|
if (!isInitializationComplete || !isSynchronized) return false;
|
||||||
|
|
||||||
|
if (isExternalSurface)
|
||||||
|
{
|
||||||
|
//Set Texture Layout
|
||||||
|
offset.x = 0;
|
||||||
|
offset.y = (int)externalSurfaceHeight;
|
||||||
|
extent.width = (int)externalSurfaceWidth;
|
||||||
|
extent.height = (int)-externalSurfaceHeight;
|
||||||
|
rect.offset = offset;
|
||||||
|
rect.extent = extent;
|
||||||
|
|
||||||
|
layerTextures.textureLayout = rect;
|
||||||
|
|
||||||
|
return true; //No need to process texture queues
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (texture != null) //check for texture size change
|
if (texture != null) //check for texture size change
|
||||||
{
|
{
|
||||||
if (TextureParamsChanged())
|
if (TextureParamsChanged())
|
||||||
@@ -457,7 +539,7 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
{
|
{
|
||||||
if (layerType == LayerType.Underlay)
|
if (layerType == LayerType.Underlay)
|
||||||
{
|
{
|
||||||
if (!enabledColorScaleBiasInShader)
|
//if (!enabledColorScaleBiasInShader)
|
||||||
{
|
{
|
||||||
if (generatedUnderlayMeshRenderer != null && generatedUnderlayMeshRenderer.material != null)
|
if (generatedUnderlayMeshRenderer != null && generatedUnderlayMeshRenderer.material != null)
|
||||||
{
|
{
|
||||||
@@ -482,6 +564,11 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
UnityToOpenXRConversionHelper.GetOpenXRColor4f(colorScale, ref CompositionLayerParamsColorScaleBias.colorScale);
|
UnityToOpenXRConversionHelper.GetOpenXRColor4f(colorScale, ref CompositionLayerParamsColorScaleBias.colorScale);
|
||||||
UnityToOpenXRConversionHelper.GetOpenXRColor4f(colorBias, ref CompositionLayerParamsColorScaleBias.colorBias);
|
UnityToOpenXRConversionHelper.GetOpenXRColor4f(colorBias, ref CompositionLayerParamsColorScaleBias.colorBias);
|
||||||
|
if (!solidEffect && enabledColorScaleBiasInShader)
|
||||||
|
{
|
||||||
|
CompositionLayerParamsColorScaleBias.colorScale.a = 1.0f;
|
||||||
|
CompositionLayerParamsColorScaleBias.colorBias.a = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
compositionLayerColorScaleBias.Submit_CompositionLayerColorBias(CompositionLayerParamsColorScaleBias, layerID);
|
compositionLayerColorScaleBias.Submit_CompositionLayerColorBias(CompositionLayerParamsColorScaleBias, layerID);
|
||||||
}
|
}
|
||||||
@@ -616,6 +703,11 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
CompositionLayerParamsQuad.type = XrStructureType.XR_TYPE_COMPOSITION_LAYER_QUAD;
|
CompositionLayerParamsQuad.type = XrStructureType.XR_TYPE_COMPOSITION_LAYER_QUAD;
|
||||||
CompositionLayerParamsQuad.layerFlags = ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT | ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
|
CompositionLayerParamsQuad.layerFlags = ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT | ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
|
||||||
|
|
||||||
|
if (!enabledColorScaleBiasInShader)
|
||||||
|
{
|
||||||
|
CompositionLayerParamsQuad.layerFlags |= ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
switch (layerVisibility)
|
switch (layerVisibility)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
@@ -683,6 +775,11 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
CompositionLayerParamsCylinder.type = XrStructureType.XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR;
|
CompositionLayerParamsCylinder.type = XrStructureType.XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR;
|
||||||
CompositionLayerParamsCylinder.layerFlags = ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT | ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
|
CompositionLayerParamsCylinder.layerFlags = ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT | ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
|
||||||
|
|
||||||
|
if (!enabledColorScaleBiasInShader)
|
||||||
|
{
|
||||||
|
CompositionLayerParamsCylinder.layerFlags |= ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
if (isHeadLock)
|
if (isHeadLock)
|
||||||
{
|
{
|
||||||
CompositionLayerParamsCylinder.space = compositionLayerFeature.HeadLockSpace;
|
CompositionLayerParamsCylinder.space = compositionLayerFeature.HeadLockSpace;
|
||||||
@@ -787,6 +884,14 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IntPtr GetExternalSurfaceObj()
|
||||||
|
{
|
||||||
|
IntPtr value = compositionLayerFeature.Compositionlayer_GetExternalSurfaceObj2(layerID);
|
||||||
|
//DEBUG("GetExternalSurfaceObj layerID " + layerID + " SurfaceObj " + value);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool RenderAsLayer()
|
public bool RenderAsLayer()
|
||||||
{
|
{
|
||||||
if (placeholderGenerated && compositionLayerPlaceholderPrefabGO != null)
|
if (placeholderGenerated && compositionLayerPlaceholderPrefabGO != null)
|
||||||
@@ -1419,7 +1524,10 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
generatedUnderlayMeshRenderer = generatedUnderlayMesh.AddComponent<MeshRenderer>();
|
generatedUnderlayMeshRenderer = generatedUnderlayMesh.AddComponent<MeshRenderer>();
|
||||||
generatedUnderlayMeshFilter = generatedUnderlayMesh.AddComponent<MeshFilter>();
|
generatedUnderlayMeshFilter = generatedUnderlayMesh.AddComponent<MeshFilter>();
|
||||||
generatedUnderlayMeshRenderer.sharedMaterial = new Material(Shader.Find("VIVE/OpenXR/CompositionLayer/UnderlayAlphaZero"));
|
if (solidEffect)
|
||||||
|
generatedUnderlayMeshRenderer.sharedMaterial = new Material(Shader.Find("VIVE/OpenXR/CompositionLayer/UnderlayAlphaZeroSolid"));
|
||||||
|
else
|
||||||
|
generatedUnderlayMeshRenderer.sharedMaterial = new Material(Shader.Find("VIVE/OpenXR/CompositionLayer/UnderlayAlphaZero"));
|
||||||
generatedUnderlayMeshRenderer.material.mainTexture = texture;
|
generatedUnderlayMeshRenderer.material.mainTexture = texture;
|
||||||
|
|
||||||
//Generate Mesh
|
//Generate Mesh
|
||||||
@@ -1441,7 +1549,10 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
generatedUnderlayMeshRenderer = generatedUnderlayMesh.AddComponent<MeshRenderer>();
|
generatedUnderlayMeshRenderer = generatedUnderlayMesh.AddComponent<MeshRenderer>();
|
||||||
generatedUnderlayMeshFilter = generatedUnderlayMesh.AddComponent<MeshFilter>();
|
generatedUnderlayMeshFilter = generatedUnderlayMesh.AddComponent<MeshFilter>();
|
||||||
generatedUnderlayMeshRenderer.material = new Material(Shader.Find("VIVE/OpenXR/CompositionLayer/UnderlayAlphaZero"));
|
if (solidEffect)
|
||||||
|
generatedUnderlayMeshRenderer.material = new Material(Shader.Find("VIVE/OpenXR/CompositionLayer/UnderlayAlphaZeroSolid"));
|
||||||
|
else
|
||||||
|
generatedUnderlayMeshRenderer.material = new Material(Shader.Find("VIVE/OpenXR/CompositionLayer/UnderlayAlphaZero"));
|
||||||
generatedUnderlayMeshRenderer.material.mainTexture = texture;
|
generatedUnderlayMeshRenderer.material.mainTexture = texture;
|
||||||
|
|
||||||
//Generate Mesh
|
//Generate Mesh
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Copyright HTC Corporation All Rights Reserved.
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -26,7 +27,13 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
private static Dictionary<int, XrCompositionLayerPassthroughHTC> passthrough2Layer = new Dictionary<int, XrCompositionLayerPassthroughHTC>();
|
||||||
|
private static Dictionary<int, IntPtr> passthrough2LayerPtr = new Dictionary<int, IntPtr>();
|
||||||
|
private static Dictionary<int, bool> passthrough2IsUnderLay= new Dictionary<int, bool>();
|
||||||
|
private static Dictionary<int, XrPassthroughMeshTransformInfoHTC> passthrough2meshTransform = new Dictionary<int, XrPassthroughMeshTransformInfoHTC>();
|
||||||
|
private static Dictionary<int, IntPtr> passthrough2meshTransformInfoPtr = new Dictionary<int, IntPtr>();
|
||||||
|
#endif
|
||||||
#region Public APIs
|
#region Public APIs
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -62,9 +69,41 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
ERROR("HTC_Passthrough feature instance not found.");
|
ERROR("HTC_Passthrough feature instance not found.");
|
||||||
return passthroughID;
|
return passthroughID;
|
||||||
}
|
}
|
||||||
|
#if UNITY_ANDROID
|
||||||
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Planar, onDestroyPassthroughSessionHandler, compositionDepth);
|
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Planar, onDestroyPassthroughSessionHandler, compositionDepth);
|
||||||
|
#endif
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
XrPassthroughHTC passthrough = 0;
|
||||||
|
XrPassthroughCreateInfoHTC createInfo = new XrPassthroughCreateInfoHTC(
|
||||||
|
XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC,
|
||||||
|
new IntPtr(6), //Enter IntPtr(0) for backward compatibility (using createPassthrough to enable the passthrough feature), or enter IntPtr(6) to enable the passthrough feature based on the layer submitted to endframe.
|
||||||
|
XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PLANAR_HTC
|
||||||
|
);
|
||||||
|
XrResult res = XR_HTC_passthrough.xrCreatePassthroughHTC(createInfo, out passthrough);
|
||||||
|
if(res == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
ulong passthrough_ulong = passthrough;
|
||||||
|
passthroughID = (int)passthrough_ulong;
|
||||||
|
XrPassthroughColorHTC passthroughColor = new XrPassthroughColorHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_PASSTHROUGH_COLOR_HTC,
|
||||||
|
in_next: IntPtr.Zero,
|
||||||
|
in_alpha: alpha);
|
||||||
|
XrCompositionLayerPassthroughHTC compositionLayerPassthrough = new XrCompositionLayerPassthroughHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC,
|
||||||
|
in_next: IntPtr.Zero,
|
||||||
|
in_layerFlags: ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT,
|
||||||
|
in_space: 0,
|
||||||
|
in_passthrough: passthrough,
|
||||||
|
in_color: passthroughColor);
|
||||||
|
passthrough2Layer.Add(passthroughID, compositionLayerPassthrough);
|
||||||
|
IntPtr layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC)));
|
||||||
|
passthrough2LayerPtr.Add(passthroughID, layerPtr);
|
||||||
|
if (layerType == LayerType.Underlay)
|
||||||
|
passthrough2IsUnderLay.Add(passthroughID, true);
|
||||||
|
if (layerType == LayerType.Overlay)
|
||||||
|
passthrough2IsUnderLay.Add(passthroughID, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (passthroughID == 0)
|
if (passthroughID == 0)
|
||||||
{
|
{
|
||||||
ERROR("Failed to create projected pasthrough");
|
ERROR("Failed to create projected pasthrough");
|
||||||
@@ -146,9 +185,57 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
ERROR("Mesh data invalid.");
|
ERROR("Mesh data invalid.");
|
||||||
return passthroughID;
|
return passthroughID;
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
XrPassthroughHTC passthrough = 0;
|
||||||
|
XrPassthroughCreateInfoHTC createInfo = new XrPassthroughCreateInfoHTC(
|
||||||
|
XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC,
|
||||||
|
new IntPtr(6), //Enter IntPtr(0) for backward compatibility (using createPassthrough to enable the passthrough feature), or enter IntPtr(6) to enable the passthrough feature based on the layer submitted to endframe.
|
||||||
|
XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PROJECTED_HTC
|
||||||
|
);
|
||||||
|
XrResult res = XR_HTC_passthrough.xrCreatePassthroughHTC(createInfo, out passthrough);
|
||||||
|
if (res == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
ulong passthrough_ulong = passthrough;
|
||||||
|
passthroughID = (int)passthrough_ulong;
|
||||||
|
XrPassthroughMeshTransformInfoHTC PassthroughMeshTransformInfo = new XrPassthroughMeshTransformInfoHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC,
|
||||||
|
in_next: IntPtr.Zero,
|
||||||
|
in_vertexCount: 0,
|
||||||
|
in_vertices: new XrVector3f[0],
|
||||||
|
in_indexCount: 0,
|
||||||
|
in_indices: new UInt32[0],
|
||||||
|
in_baseSpace: XR_HTC_passthrough.Interop.GetTrackingSpace(),
|
||||||
|
in_time: XR_HTC_passthrough.Interop.GetFrameState().predictedDisplayTime,
|
||||||
|
in_pose: new XrPosef(),
|
||||||
|
in_scale: new XrVector3f()
|
||||||
|
);
|
||||||
|
IntPtr meshTransformInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrPassthroughMeshTransformInfoHTC)));
|
||||||
|
Marshal.StructureToPtr(PassthroughMeshTransformInfo, meshTransformInfoPtr, false);
|
||||||
|
XrPassthroughColorHTC passthroughColor = new XrPassthroughColorHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_PASSTHROUGH_COLOR_HTC,
|
||||||
|
in_next: IntPtr.Zero,
|
||||||
|
in_alpha: alpha);
|
||||||
|
XrCompositionLayerPassthroughHTC compositionLayerPassthrough = new XrCompositionLayerPassthroughHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC,
|
||||||
|
in_next: meshTransformInfoPtr,
|
||||||
|
in_layerFlags: ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT,
|
||||||
|
in_space: 0,
|
||||||
|
in_passthrough: passthrough,
|
||||||
|
in_color: passthroughColor);
|
||||||
|
passthrough2meshTransform.Add(passthroughID, PassthroughMeshTransformInfo);
|
||||||
|
passthrough2meshTransformInfoPtr.Add(passthroughID, meshTransformInfoPtr);
|
||||||
|
passthrough2Layer.Add(passthroughID, compositionLayerPassthrough);
|
||||||
|
IntPtr layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC)));
|
||||||
|
passthrough2LayerPtr.Add(passthroughID, layerPtr);
|
||||||
|
if (layerType == LayerType.Underlay)
|
||||||
|
passthrough2IsUnderLay.Add(passthroughID, true);
|
||||||
|
if (layerType == LayerType.Overlay)
|
||||||
|
passthrough2IsUnderLay.Add(passthroughID, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if UNITY_ANDROID
|
||||||
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Projected, onDestroyPassthroughSessionHandler, compositionDepth);
|
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Projected, onDestroyPassthroughSessionHandler, compositionDepth);
|
||||||
|
#endif
|
||||||
if (passthroughID == 0)
|
if (passthroughID == 0)
|
||||||
{
|
{
|
||||||
ERROR("Failed to create projected pasthrough");
|
ERROR("Failed to create projected pasthrough");
|
||||||
@@ -207,9 +294,57 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
ERROR("HTC_Passthrough feature instance not found.");
|
ERROR("HTC_Passthrough feature instance not found.");
|
||||||
return passthroughID;
|
return passthroughID;
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
XrPassthroughHTC passthrough = 0;
|
||||||
|
XrPassthroughCreateInfoHTC createInfo = new XrPassthroughCreateInfoHTC(
|
||||||
|
XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC,
|
||||||
|
new IntPtr(6), //Enter IntPtr(0) for backward compatibility (using createPassthrough to enable the passthrough feature), or enter IntPtr(6) to enable the passthrough feature based on the layer submitted to endframe.
|
||||||
|
XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PROJECTED_HTC
|
||||||
|
);
|
||||||
|
XrResult res = XR_HTC_passthrough.xrCreatePassthroughHTC(createInfo, out passthrough);
|
||||||
|
if (res == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
ulong passthrough_ulong = passthrough;
|
||||||
|
passthroughID = (int)passthrough_ulong;
|
||||||
|
XrPassthroughMeshTransformInfoHTC PassthroughMeshTransformInfo = new XrPassthroughMeshTransformInfoHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC,
|
||||||
|
in_next: IntPtr.Zero,
|
||||||
|
in_vertexCount: 0,
|
||||||
|
in_vertices: new XrVector3f[0],
|
||||||
|
in_indexCount: 0,
|
||||||
|
in_indices: new UInt32[0],
|
||||||
|
in_baseSpace: XR_HTC_passthrough.Interop.GetTrackingSpace(),
|
||||||
|
in_time: XR_HTC_passthrough.Interop.GetFrameState().predictedDisplayTime,
|
||||||
|
in_pose: new XrPosef(),
|
||||||
|
in_scale: new XrVector3f()
|
||||||
|
);
|
||||||
|
IntPtr meshTransformInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrPassthroughMeshTransformInfoHTC)));
|
||||||
|
Marshal.StructureToPtr(PassthroughMeshTransformInfo, meshTransformInfoPtr, false);
|
||||||
|
XrPassthroughColorHTC passthroughColor = new XrPassthroughColorHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_PASSTHROUGH_COLOR_HTC,
|
||||||
|
in_next: IntPtr.Zero,
|
||||||
|
in_alpha: alpha);
|
||||||
|
XrCompositionLayerPassthroughHTC compositionLayerPassthrough = new XrCompositionLayerPassthroughHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC,
|
||||||
|
in_next: meshTransformInfoPtr,
|
||||||
|
in_layerFlags: ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT,
|
||||||
|
in_space: 0,
|
||||||
|
in_passthrough: passthrough,
|
||||||
|
in_color: passthroughColor);
|
||||||
|
passthrough2meshTransform.Add(passthroughID, PassthroughMeshTransformInfo);
|
||||||
|
passthrough2meshTransformInfoPtr.Add(passthroughID, meshTransformInfoPtr);
|
||||||
|
passthrough2Layer.Add(passthroughID, compositionLayerPassthrough);
|
||||||
|
IntPtr layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC)));
|
||||||
|
passthrough2LayerPtr.Add(passthroughID, layerPtr);
|
||||||
|
if (layerType == LayerType.Underlay)
|
||||||
|
passthrough2IsUnderLay.Add(passthroughID, true);
|
||||||
|
if (layerType == LayerType.Overlay)
|
||||||
|
passthrough2IsUnderLay.Add(passthroughID, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if UNITY_ANDROID
|
||||||
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Projected, onDestroyPassthroughSessionHandler);
|
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Projected, onDestroyPassthroughSessionHandler);
|
||||||
|
#endif
|
||||||
if (passthroughID == 0)
|
if (passthroughID == 0)
|
||||||
{
|
{
|
||||||
ERROR("Failed to create projected pasthrough");
|
ERROR("Failed to create projected pasthrough");
|
||||||
@@ -221,6 +356,25 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
return passthroughID;
|
return passthroughID;
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
private static void SubmitLayer()
|
||||||
|
{
|
||||||
|
XR_HTC_passthrough.Interop.GetOriginEndFrameLayerList(out List<IntPtr> layerList);//GetOriginEndFrameLayers
|
||||||
|
foreach(var passthrough in passthrough2IsUnderLay.Keys)
|
||||||
|
{
|
||||||
|
//Get and submit layer list
|
||||||
|
if (layerList.Count != 0)
|
||||||
|
{
|
||||||
|
Marshal.StructureToPtr(passthrough2Layer[passthrough], passthrough2LayerPtr[passthrough], false);
|
||||||
|
if (passthrough2IsUnderLay[passthrough])
|
||||||
|
layerList.Insert(0, passthrough2LayerPtr[passthrough]);
|
||||||
|
else
|
||||||
|
layerList.Insert(1, passthrough2LayerPtr[passthrough]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XR_HTC_passthrough.Interop.SubmitLayers(layerList);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For destroying a passthrough created previously.
|
/// For destroying a passthrough created previously.
|
||||||
@@ -239,14 +393,28 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
ERROR("HTC_Passthrough feature instance not found.");
|
ERROR("HTC_Passthrough feature instance not found.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!passthroughFeature.PassthroughIDList.Contains(passthroughID))
|
||||||
|
{
|
||||||
|
ERROR("Passthrough to be destroyed not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
XrPassthroughHTC passthrough = passthrough2Layer[passthroughID].passthrough;
|
||||||
|
XR_HTC_passthrough.xrDestroyPassthroughHTC(passthrough);
|
||||||
|
passthrough2IsUnderLay.Remove(passthroughID);
|
||||||
|
SubmitLayer();
|
||||||
|
passthrough2Layer.Remove(passthroughID);
|
||||||
|
if(passthrough2LayerPtr.ContainsKey(passthroughID)) Marshal.FreeHGlobal(passthrough2LayerPtr[passthroughID]);
|
||||||
|
passthrough2LayerPtr.Remove(passthroughID);
|
||||||
|
if(passthrough2meshTransformInfoPtr.ContainsKey(passthroughID)) Marshal.FreeHGlobal(passthrough2meshTransformInfoPtr[passthroughID]);
|
||||||
|
passthrough2meshTransformInfoPtr.Remove(passthroughID);
|
||||||
|
passthrough2meshTransform.Remove(passthroughID);
|
||||||
|
|
||||||
if (!passthroughFeature.PassthroughIDList.Contains(passthroughID))
|
return true;
|
||||||
{
|
#endif
|
||||||
ERROR("Passthrough to be destroyed not found");
|
#if UNITY_ANDROID
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return passthroughFeature.HTCPassthrough_DestroyPassthrough(passthroughID);
|
return passthroughFeature.HTCPassthrough_DestroyPassthrough(passthroughID);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -277,7 +445,7 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
ERROR("HTC_Passthrough feature instance not found.");
|
ERROR("HTC_Passthrough feature instance not found.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#if UNITY_ANDROID
|
||||||
if (autoClamp)
|
if (autoClamp)
|
||||||
{
|
{
|
||||||
return passthroughFeature.HTCPassthrough_SetAlpha(passthroughID, Mathf.Clamp01(alpha));
|
return passthroughFeature.HTCPassthrough_SetAlpha(passthroughID, Mathf.Clamp01(alpha));
|
||||||
@@ -292,6 +460,19 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
return passthroughFeature.HTCPassthrough_SetAlpha(passthroughID, alpha);
|
return passthroughFeature.HTCPassthrough_SetAlpha(passthroughID, alpha);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (passthrough2Layer.ContainsKey(passthroughID))
|
||||||
|
{
|
||||||
|
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||||
|
layer.color.alpha = alpha;
|
||||||
|
passthrough2Layer[passthroughID] = layer;
|
||||||
|
SubmitLayer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -340,10 +521,29 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
{
|
{
|
||||||
indexBufferUint[i] = (uint)indexBuffer[i];
|
indexBufferUint[i] = (uint)indexBuffer[i];
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||||
|
{
|
||||||
|
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||||
|
MeshTransformInfo.vertexCount = (uint)vertexBuffer.Length;
|
||||||
|
MeshTransformInfo.vertices = vertexBufferXrVector;
|
||||||
|
MeshTransformInfo.indexCount = (uint)indexBuffer.Length;
|
||||||
|
MeshTransformInfo.indices = indexBufferUint;
|
||||||
|
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||||
|
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||||
|
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||||
|
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||||
|
passthrough2Layer[passthroughID] = layer;
|
||||||
|
SubmitLayer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
//Note: Ignore Clock-Wise definition of index buffer for now as passthrough extension does not have back-face culling
|
//Note: Ignore Clock-Wise definition of index buffer for now as passthrough extension does not have back-face culling
|
||||||
|
#if UNITY_ANDROID
|
||||||
return passthroughFeature.HTCPassthrough_SetMesh(passthroughID, (uint)vertexBuffer.Length, vertexBufferXrVector, (uint)indexBuffer.Length, indexBufferUint); ;
|
return passthroughFeature.HTCPassthrough_SetMesh(passthroughID, (uint)vertexBuffer.Length, vertexBufferXrVector, (uint)indexBuffer.Length, indexBufferUint); ;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -402,8 +602,26 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
meshXrPose.orientation = OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR);
|
meshXrPose.orientation = OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR);
|
||||||
|
|
||||||
XrVector3f meshXrScale = OpenXRHelper.ToOpenXRVector(meshScale, false);
|
XrVector3f meshXrScale = OpenXRHelper.ToOpenXRVector(meshScale, false);
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||||
|
{
|
||||||
|
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||||
|
MeshTransformInfo.pose = meshXrPose;
|
||||||
|
MeshTransformInfo.scale = meshXrScale;
|
||||||
|
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||||
|
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||||
|
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||||
|
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||||
|
passthrough2Layer[passthroughID] = layer;
|
||||||
|
SubmitLayer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
#if UNITY_ANDROID
|
||||||
return passthroughFeature.HTCPassthrough_SetMeshTransform(passthroughID, passthroughFeature.GetXrSpaceFromSpaceType(spaceType), meshXrPose, meshXrScale);
|
return passthroughFeature.HTCPassthrough_SetMeshTransform(passthroughID, passthroughFeature.GetXrSpaceFromSpaceType(spaceType), meshXrPose, meshXrScale);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -429,8 +647,20 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
ERROR("HTC_Passthrough feature instance not found.");
|
ERROR("HTC_Passthrough feature instance not found.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (passthrough2IsUnderLay.ContainsKey(passthroughID))
|
||||||
|
{
|
||||||
|
passthrough2IsUnderLay[passthroughID] = layerType == LayerType.Underlay ? true : false;
|
||||||
|
SubmitLayer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
#if UNITY_ANDROID
|
||||||
return passthroughFeature.HTCPassthrough_SetLayerType(passthroughID, layerType, compositionDepth);
|
return passthroughFeature.HTCPassthrough_SetLayerType(passthroughID, layerType, compositionDepth);
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -452,8 +682,25 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
ERROR("HTC_Passthrough feature instance not found.");
|
ERROR("HTC_Passthrough feature instance not found.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||||
|
{
|
||||||
|
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||||
|
MeshTransformInfo.baseSpace = passthroughFeature.GetXrSpaceFromSpaceType(spaceType);
|
||||||
|
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||||
|
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||||
|
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||||
|
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||||
|
passthrough2Layer[passthroughID] = layer;
|
||||||
|
SubmitLayer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
#if UNITY_ANDROID
|
||||||
return passthroughFeature.HTCPassthrough_SetMeshTransformSpace(passthroughID, passthroughFeature.GetXrSpaceFromSpaceType(spaceType));
|
return passthroughFeature.HTCPassthrough_SetMeshTransformSpace(passthroughID, passthroughFeature.GetXrSpaceFromSpaceType(spaceType));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -495,8 +742,27 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
trackingSpaceMeshPosition = trackingSpaceLayerPoseTRS.GetColumn(3); //4th Column of TRS Matrix is the position
|
trackingSpaceMeshPosition = trackingSpaceLayerPoseTRS.GetColumn(3); //4th Column of TRS Matrix is the position
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||||
|
{
|
||||||
|
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||||
|
XrPosef meshXrPose = MeshTransformInfo.pose;
|
||||||
|
meshXrPose.position = OpenXRHelper.ToOpenXRVector(trackingSpaceMeshPosition, convertFromUnityToOpenXR); ;
|
||||||
|
MeshTransformInfo.pose = meshXrPose;
|
||||||
|
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||||
|
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||||
|
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||||
|
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||||
|
passthrough2Layer[passthroughID] = layer;
|
||||||
|
SubmitLayer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
#if UNITY_ANDROID
|
||||||
return passthroughFeature.HTCPassthrough_SetMeshTransformPosition(passthroughID, OpenXRHelper.ToOpenXRVector(trackingSpaceMeshPosition, convertFromUnityToOpenXR));
|
return passthroughFeature.HTCPassthrough_SetMeshTransformPosition(passthroughID, OpenXRHelper.ToOpenXRVector(trackingSpaceMeshPosition, convertFromUnityToOpenXR));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -538,8 +804,27 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
trackingSpaceMeshRotation = Quaternion.LookRotation(trackingSpaceLayerPoseTRS.GetColumn(2), trackingSpaceLayerPoseTRS.GetColumn(1));
|
trackingSpaceMeshRotation = Quaternion.LookRotation(trackingSpaceLayerPoseTRS.GetColumn(2), trackingSpaceLayerPoseTRS.GetColumn(1));
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||||
|
{
|
||||||
|
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||||
|
XrPosef meshXrPose = MeshTransformInfo.pose;
|
||||||
|
meshXrPose.orientation = OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR);
|
||||||
|
MeshTransformInfo.pose = meshXrPose;
|
||||||
|
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||||
|
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||||
|
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||||
|
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||||
|
passthrough2Layer[passthroughID] = layer;
|
||||||
|
SubmitLayer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
#if UNITY_ANDROID
|
||||||
return passthroughFeature.HTCPassthrough_SetMeshTransformOrientation(passthroughID, OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR));
|
return passthroughFeature.HTCPassthrough_SetMeshTransformOrientation(passthroughID, OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -561,8 +846,26 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
ERROR("HTC_Passthrough feature instance not found.");
|
ERROR("HTC_Passthrough feature instance not found.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||||
|
{
|
||||||
|
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||||
|
MeshTransformInfo.scale = OpenXRHelper.ToOpenXRVector(meshScale, false);
|
||||||
|
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||||
|
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||||
|
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||||
|
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||||
|
passthrough2Layer[passthroughID] = layer;
|
||||||
|
SubmitLayer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if UNITY_ANDROID
|
||||||
return passthroughFeature.HTCPassthrough_SetMeshTransformScale(passthroughID, OpenXRHelper.ToOpenXRVector(meshScale, false));
|
return passthroughFeature.HTCPassthrough_SetMeshTransformScale(passthroughID, OpenXRHelper.ToOpenXRVector(meshScale, false));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -581,6 +884,6 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
return passthroughFeature.PassthroughIDList;
|
return passthroughFeature.PassthroughIDList;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: ac2dcc122c218e7419c571b30aad6bbc
|
guid: 673b5df0bff21a84c8b23a4f3c6a6268
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 046b5fd65fa996041a970e1fd193d213
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 69ae1c3151561af42ba226f0e563ebc6
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: c44051e3658496a428f4b92daa752ebf
|
guid: c5cbfbcf56aaffa4fab38659c00c3903
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
// Remove FAKE_DATA if editor or windows is supported.
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
#define FAKE_DATA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.XR.OpenXR;
|
||||||
|
using UnityEngine.XR.OpenXR.Features;
|
||||||
|
using VIVE.OpenXR.Feature;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.XR.OpenXR.Features;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.Anchor
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[OpenXRFeature(UiName = "VIVE XR Anchor",
|
||||||
|
Desc = "VIVE's implementaion of the XR_HTC_anchor.",
|
||||||
|
Company = "HTC",
|
||||||
|
DocumentationLink = "..\\Documentation",
|
||||||
|
OpenxrExtensionStrings = kOpenxrExtensionString,
|
||||||
|
Version = "1.0.0",
|
||||||
|
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||||
|
FeatureId = featureId
|
||||||
|
)]
|
||||||
|
#endif
|
||||||
|
public class ViveAnchor : OpenXRFeature
|
||||||
|
{
|
||||||
|
public const string kOpenxrExtensionString = "XR_HTC_anchor";
|
||||||
|
/// <summary>
|
||||||
|
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||||
|
/// </summary>
|
||||||
|
public const string featureId = "vive.wave.openxr.feature.htcanchor";
|
||||||
|
private XrInstance m_XrInstance = 0;
|
||||||
|
private XrSession session = 0;
|
||||||
|
private XrSystemId m_XrSystemId = 0;
|
||||||
|
|
||||||
|
#region struct, enum, const of this extensions
|
||||||
|
|
||||||
|
public struct XrSystemAnchorPropertiesHTC
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public System.IntPtr next;
|
||||||
|
public XrBool32 supportsAnchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||||
|
public struct XrSpatialAnchorNameHTC
|
||||||
|
{
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
||||||
|
public string name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct XrSpatialAnchorCreateInfoHTC
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public System.IntPtr next;
|
||||||
|
public XrSpace space;
|
||||||
|
public XrPosef poseInSpace;
|
||||||
|
public XrSpatialAnchorNameHTC name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region delegates and delegate instances
|
||||||
|
delegate XrResult DelegateXrCreateSpatialAnchorHTC(XrSession session, ref XrSpatialAnchorCreateInfoHTC createInfo, ref XrSpace anchor);
|
||||||
|
delegate XrResult DelegateXrGetSpatialAnchorNameHTC(XrSpace anchor, ref XrSpatialAnchorNameHTC name);
|
||||||
|
|
||||||
|
DelegateXrCreateSpatialAnchorHTC XrCreateSpatialAnchorHTC;
|
||||||
|
DelegateXrGetSpatialAnchorNameHTC XrGetSpatialAnchorNameHTC;
|
||||||
|
#endregion delegates and delegate instances
|
||||||
|
|
||||||
|
#region override functions
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
|
{
|
||||||
|
Debug.Log("ViveAnchor HookGetInstanceProcAddr() ");
|
||||||
|
return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||||
|
{
|
||||||
|
//Debug.Log("VIVEAnchor OnInstanceCreate() ");
|
||||||
|
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
||||||
|
{
|
||||||
|
Debug.LogWarning("ViveAnchor OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_XrInstance = xrInstance;
|
||||||
|
//Debug.Log("OnInstanceCreate() " + m_XrInstance);
|
||||||
|
CommonWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
|
||||||
|
SpaceWrapper.Instance.OnInstanceCreate(xrInstance, CommonWrapper.Instance.GetInstanceProcAddr);
|
||||||
|
|
||||||
|
return GetXrFunctionDelegates(m_XrInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||||
|
{
|
||||||
|
CommonWrapper.Instance.OnInstanceDestroy();
|
||||||
|
SpaceWrapper.Instance.OnInstanceDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnSessionCreate(ulong xrSession)
|
||||||
|
{
|
||||||
|
Debug.Log("ViveAnchor OnSessionCreate() ");
|
||||||
|
|
||||||
|
// here's one way you can grab the session
|
||||||
|
Debug.Log($"EXT: Got xrSession: {xrSession}");
|
||||||
|
session = xrSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnSessionBegin(ulong xrSession)
|
||||||
|
{
|
||||||
|
Debug.Log("ViveAnchor OnSessionBegin() ");
|
||||||
|
Debug.Log($"EXT: xrBeginSession: {xrSession}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnSessionEnd(ulong xrSession)
|
||||||
|
{
|
||||||
|
Debug.Log("ViveAnchor OnSessionEnd() ");
|
||||||
|
Debug.Log($"EXT: about to xrEndSession: {xrSession}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX Every millisecond the AppSpace switched from one space to another space. I don't know what is going on.
|
||||||
|
//private ulong appSpace;
|
||||||
|
//protected override void OnAppSpaceChange(ulong space)
|
||||||
|
//{
|
||||||
|
// //Debug.Log($"VIVEAnchor OnAppSpaceChange({appSpace} -> {space})");
|
||||||
|
// appSpace = space;
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnSystemChange(ulong xrSystem)
|
||||||
|
{
|
||||||
|
m_XrSystemId = xrSystem;
|
||||||
|
Debug.Log("ViveAnchor OnSystemChange() " + m_XrSystemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion override functions
|
||||||
|
|
||||||
|
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||||
|
{
|
||||||
|
Debug.Log("ViveAnchor GetXrFunctionDelegates() ");
|
||||||
|
|
||||||
|
bool ret = true;
|
||||||
|
IntPtr funcPtr = IntPtr.Zero;
|
||||||
|
OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr = CommonWrapper.Instance.GetInstanceProcAddr; // shorter name
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrCreateSpatialAnchorHTC", out XrCreateSpatialAnchorHTC);
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrGetSpatialAnchorNameHTC", out XrGetSpatialAnchorNameHTC);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region functions of extension
|
||||||
|
/// <summary>
|
||||||
|
/// Helper function to get this feature' properties.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||||
|
/// </summary>
|
||||||
|
public XrResult GetProperties(out XrSystemAnchorPropertiesHTC anchorProperties)
|
||||||
|
{
|
||||||
|
anchorProperties = new XrSystemAnchorPropertiesHTC();
|
||||||
|
anchorProperties.type = XrStructureType.XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC;
|
||||||
|
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
{
|
||||||
|
anchorProperties.type = XrStructureType.XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC;
|
||||||
|
anchorProperties.supportsAnchor = true;
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return CommonWrapper.Instance.GetProperties(m_XrInstance, m_XrSystemId, ref anchorProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XrResult CreateSpatialAnchor(XrSpatialAnchorCreateInfoHTC createInfo, out XrSpace anchor)
|
||||||
|
{
|
||||||
|
anchor = default;
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
#endif
|
||||||
|
var ret = XrCreateSpatialAnchorHTC(session, ref createInfo, ref anchor);
|
||||||
|
Debug.Log("ViveAnchor CreateSpatialAnchor() r=" + ret + ", a=" + anchor + ", bs=" + createInfo.space +
|
||||||
|
", pos=(" + createInfo.poseInSpace.position.x + "," + createInfo.poseInSpace.position.y + "," + createInfo.poseInSpace.position.z +
|
||||||
|
"), rot=(" + createInfo.poseInSpace.orientation.x + "," + createInfo.poseInSpace.orientation.y + "," + createInfo.poseInSpace.orientation.z + "," + createInfo.poseInSpace.orientation.w +
|
||||||
|
"), n=" + createInfo.name.name);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XrResult GetSpatialAnchorName(XrSpace anchor, out XrSpatialAnchorNameHTC name)
|
||||||
|
{
|
||||||
|
name = default;
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
{
|
||||||
|
name.name = "fake anchor";
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return XrGetSpatialAnchorNameHTC(anchor, ref name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region tools for user
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// According to XRInputSubsystem's tracking origin mode, return the corresponding XrSpace.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrSpace GetTrackingSpace()
|
||||||
|
{
|
||||||
|
var s = GetCurrentAppSpace();
|
||||||
|
Debug.Log("ViveAnchor GetTrackingSpace() s=" + s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c17aa731a6f4fb54bb9a2c28df667e5e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright HTC Corporation All Rights Reserved.
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
@@ -48,6 +48,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
#region OpenXR Life Cycle
|
#region OpenXR Life Cycle
|
||||||
private bool m_XrInstanceCreated = false;
|
private bool m_XrInstanceCreated = false;
|
||||||
|
/// <summary>
|
||||||
|
/// The XR instance is created or not.
|
||||||
|
/// </summary>
|
||||||
public bool XrInstanceCreated
|
public bool XrInstanceCreated
|
||||||
{
|
{
|
||||||
get { return m_XrInstanceCreated; }
|
get { return m_XrInstanceCreated; }
|
||||||
@@ -55,13 +58,13 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
private XrInstance m_XrInstance = 0;
|
private XrInstance m_XrInstance = 0;
|
||||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||||
{
|
{
|
||||||
//foreach (string kOpenxrExtensionString in kOpenxrExtensionStrings.Split(' '))
|
foreach (string kOpenxrExtensionString in kOpenxrExtensionStrings.Split(' '))
|
||||||
//{
|
{
|
||||||
// if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
||||||
// {
|
{
|
||||||
// WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
m_XrInstanceCreated = true;
|
m_XrInstanceCreated = true;
|
||||||
m_XrInstance = xrInstance;
|
m_XrInstance = xrInstance;
|
||||||
@@ -84,6 +87,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
private bool m_XrSessionCreated = false;
|
private bool m_XrSessionCreated = false;
|
||||||
|
/// <summary>
|
||||||
|
/// The XR session is created or not.
|
||||||
|
/// </summary>
|
||||||
public bool XrSessionCreated
|
public bool XrSessionCreated
|
||||||
{
|
{
|
||||||
get { return m_XrSessionCreated; }
|
get { return m_XrSessionCreated; }
|
||||||
@@ -97,20 +103,32 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
private bool m_XrSessionEnding = false;
|
private bool m_XrSessionEnding = false;
|
||||||
|
/// <summary>
|
||||||
|
/// The XR session is ending or not.
|
||||||
|
/// </summary>
|
||||||
public bool XrSessionEnding
|
public bool XrSessionEnding
|
||||||
{
|
{
|
||||||
get { return m_XrSessionEnding; }
|
get { return m_XrSessionEnding; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private XrSpace m_WorldLockSpaceOriginOnHead = 0, m_WorldLockSpaceOriginOnFloor = 0, m_HeadLockSpace = 0;
|
private XrSpace m_WorldLockSpaceOriginOnHead = 0, m_WorldLockSpaceOriginOnFloor = 0, m_HeadLockSpace = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// The XrSpace of world lock space origin on head.
|
||||||
|
/// </summary>
|
||||||
public XrSpace WorldLockSpaceOriginOnHead
|
public XrSpace WorldLockSpaceOriginOnHead
|
||||||
{
|
{
|
||||||
get { return m_WorldLockSpaceOriginOnHead; }
|
get { return m_WorldLockSpaceOriginOnHead; }
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The XrSpace of world lock space origin on floor.
|
||||||
|
/// </summary>
|
||||||
public XrSpace WorldLockSpaceOriginOnFloor
|
public XrSpace WorldLockSpaceOriginOnFloor
|
||||||
{
|
{
|
||||||
get { return m_WorldLockSpaceOriginOnFloor; }
|
get { return m_WorldLockSpaceOriginOnFloor; }
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The XrSpace of head lock space.
|
||||||
|
/// </summary>
|
||||||
public XrSpace HeadLockSpace
|
public XrSpace HeadLockSpace
|
||||||
{
|
{
|
||||||
get { return m_HeadLockSpace; }
|
get { return m_HeadLockSpace; }
|
||||||
@@ -252,6 +270,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current XR Session state.
|
||||||
|
/// </summary>
|
||||||
public XrSessionState XrSessionCurrentState
|
public XrSessionState XrSessionCurrentState
|
||||||
{
|
{
|
||||||
get { return m_XrSessionNewState; }
|
get { return m_XrSessionNewState; }
|
||||||
@@ -289,6 +310,10 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
/// xrGetSystemProperties
|
/// xrGetSystemProperties
|
||||||
OpenXRHelper.xrGetSystemPropertiesDelegate xrGetSystemProperties;
|
OpenXRHelper.xrGetSystemPropertiesDelegate xrGetSystemProperties;
|
||||||
|
/// <summary>
|
||||||
|
/// Helper function to get this feature' properties.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||||
|
/// </summary>
|
||||||
public XrResult GetSystemProperties(ref XrSystemProperties properties)
|
public XrResult GetSystemProperties(ref XrSystemProperties properties)
|
||||||
{
|
{
|
||||||
if (m_XrInstanceCreated)
|
if (m_XrInstanceCreated)
|
||||||
@@ -301,6 +326,10 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
/// xrEnumerateReferenceSpaces
|
/// xrEnumerateReferenceSpaces
|
||||||
OpenXRHelper.xrEnumerateReferenceSpacesDelegate xrEnumerateReferenceSpaces;
|
OpenXRHelper.xrEnumerateReferenceSpacesDelegate xrEnumerateReferenceSpaces;
|
||||||
|
/// <summary>
|
||||||
|
/// Enumerate available reference spaces
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrEnumerateReferenceSpaces.html">xrEnumerateReferenceSpaces</see>
|
||||||
|
/// </summary>
|
||||||
public XrResult EnumerateReferenceSpaces(UInt32 spaceCapacityInput, out UInt32 spaceCountOutput, out XrReferenceSpaceType spaces)
|
public XrResult EnumerateReferenceSpaces(UInt32 spaceCapacityInput, out UInt32 spaceCountOutput, out XrReferenceSpaceType spaces)
|
||||||
{
|
{
|
||||||
if (!m_XrSessionCreated)
|
if (!m_XrSessionCreated)
|
||||||
@@ -315,6 +344,10 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
/// xrCreateReferenceSpace
|
/// xrCreateReferenceSpace
|
||||||
OpenXRHelper.xrCreateReferenceSpaceDelegate xrCreateReferenceSpace;
|
OpenXRHelper.xrCreateReferenceSpaceDelegate xrCreateReferenceSpace;
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a reference space
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrCreateReferenceSpace.html">xrCreateReferenceSpace</see>
|
||||||
|
/// </summary>
|
||||||
public XrResult CreateReferenceSpace(ref XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
public XrResult CreateReferenceSpace(ref XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
||||||
{
|
{
|
||||||
if (!m_XrSessionCreated)
|
if (!m_XrSessionCreated)
|
||||||
@@ -328,6 +361,10 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
/// xrDestroySpace
|
/// xrDestroySpace
|
||||||
OpenXRHelper.xrDestroySpaceDelegate xrDestroySpace;
|
OpenXRHelper.xrDestroySpaceDelegate xrDestroySpace;
|
||||||
|
/// <summary>
|
||||||
|
/// Destroys an XrSpace
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrDestroySpace.html">xrDestroySpace</see>
|
||||||
|
/// </summary>
|
||||||
public XrResult DestroySpace(XrSpace space)
|
public XrResult DestroySpace(XrSpace space)
|
||||||
{
|
{
|
||||||
if (space != 0)
|
if (space != 0)
|
||||||
@@ -435,9 +472,26 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
#region Wrapper Functions
|
#region Wrapper Functions
|
||||||
private const string ExtLib = "viveopenxr";
|
private const string ExtLib = "viveopenxr";
|
||||||
|
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_GetExternalSurfaceObj")]
|
||||||
|
public static extern IntPtr VIVEOpenXR_Compositionlayer_GetExternalSurfaceObj();
|
||||||
|
public IntPtr Compositionlayer_GetExternalSurfaceObj()
|
||||||
|
{
|
||||||
|
return VIVEOpenXR_Compositionlayer_GetExternalSurfaceObj();
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_GetExternalSurfaceObj2")]
|
||||||
|
public static extern IntPtr VIVEOpenXR_Compositionlayer_GetExternalSurfaceObj2(int layerID);
|
||||||
|
public IntPtr Compositionlayer_GetExternalSurfaceObj2(int layerID)
|
||||||
|
{
|
||||||
|
return VIVEOpenXR_Compositionlayer_GetExternalSurfaceObj2(layerID);
|
||||||
|
}
|
||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_Init")]
|
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_Init")]
|
||||||
public static extern int VIVEOpenXR_CompositionLayer_Init(XrSession session, uint textureWidth, uint textureHeight, GraphicsAPI graphicsAPI, bool isDynamic, bool isProtected, out uint imageCount);
|
public static extern int VIVEOpenXR_CompositionLayer_Init(XrSession session, uint textureWidth, uint textureHeight, GraphicsAPI graphicsAPI, bool isDynamic, bool isProtected, out uint imageCount, bool isExternal);
|
||||||
public int CompositionLayer_Init(uint textureWidth, uint textureHeight, GraphicsAPI graphicsAPI, bool isDynamic, bool isProtected, out uint imageCount)
|
/// <summary>
|
||||||
|
/// Init composion layer.
|
||||||
|
/// </summary>
|
||||||
|
public int CompositionLayer_Init(uint textureWidth, uint textureHeight, GraphicsAPI graphicsAPI, bool isDynamic, bool isProtected, out uint imageCount, bool isExternal = false)
|
||||||
{
|
{
|
||||||
if (!m_XrSessionCreated)
|
if (!m_XrSessionCreated)
|
||||||
{
|
{
|
||||||
@@ -446,11 +500,14 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return VIVEOpenXR_CompositionLayer_Init(m_XrSession, textureWidth, textureHeight, graphicsAPI, isDynamic, isProtected, out imageCount);
|
return VIVEOpenXR_CompositionLayer_Init(m_XrSession, textureWidth, textureHeight, graphicsAPI, isDynamic, isProtected, out imageCount, isExternal);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_GetTexture")]
|
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_GetTexture")]
|
||||||
public static extern IntPtr VIVEOpenXR_CompositionLayer_GetTexture(int layerID, out uint imageIndex);
|
public static extern IntPtr VIVEOpenXR_CompositionLayer_GetTexture(int layerID, out uint imageIndex);
|
||||||
|
/// <summary>
|
||||||
|
/// Get composition layer texture.
|
||||||
|
/// </summary>
|
||||||
public IntPtr CompositionLayer_GetTexture(int layerID, out uint imageIndex)
|
public IntPtr CompositionLayer_GetTexture(int layerID, out uint imageIndex)
|
||||||
{
|
{
|
||||||
return VIVEOpenXR_CompositionLayer_GetTexture(layerID, out imageIndex);
|
return VIVEOpenXR_CompositionLayer_GetTexture(layerID, out imageIndex);
|
||||||
@@ -458,6 +515,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_ReleaseTexture")]
|
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_ReleaseTexture")]
|
||||||
public static extern bool VIVEOpenXR_CompositionLayer_ReleaseTexture(int layerID);
|
public static extern bool VIVEOpenXR_CompositionLayer_ReleaseTexture(int layerID);
|
||||||
|
/// <summary>
|
||||||
|
/// release composition layer texture.
|
||||||
|
/// </summary>
|
||||||
public bool CompositionLayer_ReleaseTexture(int layerID)
|
public bool CompositionLayer_ReleaseTexture(int layerID)
|
||||||
{
|
{
|
||||||
return VIVEOpenXR_CompositionLayer_ReleaseTexture(layerID);
|
return VIVEOpenXR_CompositionLayer_ReleaseTexture(layerID);
|
||||||
@@ -465,6 +525,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_Destroy")]
|
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_Destroy")]
|
||||||
public static extern bool VIVEOpenXR_CompositionLayer_Destroy(int layerID);
|
public static extern bool VIVEOpenXR_CompositionLayer_Destroy(int layerID);
|
||||||
|
/// <summary>
|
||||||
|
/// destroy composition layer.
|
||||||
|
/// </summary>
|
||||||
public bool CompositionLayer_Destroy(int layerID)
|
public bool CompositionLayer_Destroy(int layerID)
|
||||||
{
|
{
|
||||||
return VIVEOpenXR_CompositionLayer_Destroy(layerID);
|
return VIVEOpenXR_CompositionLayer_Destroy(layerID);
|
||||||
@@ -472,6 +535,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerQuad")]
|
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerQuad")]
|
||||||
public static extern void VIVEOpenXR_Submit_CompositionLayerQuad(XrCompositionLayerQuad quad, LayerType layerType, uint compositionDepth, int layerID);
|
public static extern void VIVEOpenXR_Submit_CompositionLayerQuad(XrCompositionLayerQuad quad, LayerType layerType, uint compositionDepth, int layerID);
|
||||||
|
/// <summary>
|
||||||
|
/// submit compostion layer of type quad.
|
||||||
|
/// </summary>
|
||||||
public void Submit_CompositionLayerQuad(XrCompositionLayerQuad quad, LayerType layerType, uint compositionDepth, int layerID)
|
public void Submit_CompositionLayerQuad(XrCompositionLayerQuad quad, LayerType layerType, uint compositionDepth, int layerID)
|
||||||
{
|
{
|
||||||
VIVEOpenXR_Submit_CompositionLayerQuad(quad, layerType, compositionDepth, layerID);
|
VIVEOpenXR_Submit_CompositionLayerQuad(quad, layerType, compositionDepth, layerID);
|
||||||
@@ -491,6 +557,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_GetFuncAddrs")]
|
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_GetFuncAddrs")]
|
||||||
public static extern XrResult VIVEOpenXR_CompositionLayer_GetFuncAddrs(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrFuncPtr);
|
public static extern XrResult VIVEOpenXR_CompositionLayer_GetFuncAddrs(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrFuncPtr);
|
||||||
|
/// <summary>
|
||||||
|
/// get function address of composition layer.
|
||||||
|
/// </summary>
|
||||||
public XrResult CompositionLayer_GetFuncAddrs(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrFuncPtr)
|
public XrResult CompositionLayer_GetFuncAddrs(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrFuncPtr)
|
||||||
{
|
{
|
||||||
return VIVEOpenXR_CompositionLayer_GetFuncAddrs(xrInstance, xrGetInstanceProcAddrFuncPtr);
|
return VIVEOpenXR_CompositionLayer_GetFuncAddrs(xrInstance, xrGetInstanceProcAddrFuncPtr);
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
private const string kOpenXRColorScaleBiasExtensionString = "XR_KHR_composition_layer_color_scale_bias";
|
private const string kOpenXRColorScaleBiasExtensionString = "XR_KHR_composition_layer_color_scale_bias";
|
||||||
|
|
||||||
private bool m_ColorScaleBiasExtensionEnabled = true;
|
private bool m_ColorScaleBiasExtensionEnabled = true;
|
||||||
|
/// <summary>
|
||||||
|
/// The extension of "XR_KHR_composition_layer_color_scale_bias" is enabled or not.
|
||||||
|
/// </summary>
|
||||||
public bool ColorScaleBiasExtensionEnabled
|
public bool ColorScaleBiasExtensionEnabled
|
||||||
{
|
{
|
||||||
get { return m_ColorScaleBiasExtensionEnabled; }
|
get { return m_ColorScaleBiasExtensionEnabled; }
|
||||||
@@ -68,6 +71,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerColorBias")]
|
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerColorBias")]
|
||||||
public static extern void VIVEOpenXR_Submit_CompositionLayerColorBias(XrCompositionLayerColorScaleBiasKHR colorBias, int layerID);
|
public static extern void VIVEOpenXR_Submit_CompositionLayerColorBias(XrCompositionLayerColorScaleBiasKHR colorBias, int layerID);
|
||||||
|
/// <summary>
|
||||||
|
/// Submit Compostion Layer Color Bias Settings to specific layer ID.
|
||||||
|
/// </summary>
|
||||||
public void Submit_CompositionLayerColorBias(XrCompositionLayerColorScaleBiasKHR colorBias, int layerID)
|
public void Submit_CompositionLayerColorBias(XrCompositionLayerColorScaleBiasKHR colorBias, int layerID)
|
||||||
{
|
{
|
||||||
if (!ColorScaleBiasExtensionEnabled)
|
if (!ColorScaleBiasExtensionEnabled)
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
private const string kOpenXRCylinderExtensionString = "XR_KHR_composition_layer_cylinder";
|
private const string kOpenXRCylinderExtensionString = "XR_KHR_composition_layer_cylinder";
|
||||||
|
|
||||||
private bool m_CylinderExtensionEnabled = true;
|
private bool m_CylinderExtensionEnabled = true;
|
||||||
|
/// <summary>
|
||||||
|
/// The extension "XR_KHR_composition_layer_cylinder" is enabled or not.
|
||||||
|
/// </summary>
|
||||||
public bool CylinderExtensionEnabled
|
public bool CylinderExtensionEnabled
|
||||||
{
|
{
|
||||||
get { return m_CylinderExtensionEnabled; }
|
get { return m_CylinderExtensionEnabled; }
|
||||||
@@ -68,6 +71,9 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerCylinder")]
|
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerCylinder")]
|
||||||
public static extern void VIVEOpenXR_Submit_CompositionLayerCylinder(XrCompositionLayerCylinderKHR cylinder, LayerType layerType, uint compositionDepth, int layerID);
|
public static extern void VIVEOpenXR_Submit_CompositionLayerCylinder(XrCompositionLayerCylinderKHR cylinder, LayerType layerType, uint compositionDepth, int layerID);
|
||||||
|
/// <summary>
|
||||||
|
/// submit compostion layer of type cylinder.
|
||||||
|
/// </summary>
|
||||||
public void Submit_CompositionLayerCylinder(XrCompositionLayerCylinderKHR cylinder, LayerType layerType, uint compositionDepth, int layerID)
|
public void Submit_CompositionLayerCylinder(XrCompositionLayerCylinderKHR cylinder, LayerType layerType, uint compositionDepth, int layerID)
|
||||||
{
|
{
|
||||||
if (!CylinderExtensionEnabled)
|
if (!CylinderExtensionEnabled)
|
||||||
|
|||||||
@@ -301,8 +301,288 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
Underlay = 2
|
Underlay = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An application can create an <see cref="XrPassthroughHTC">XrPassthroughHTC</see> handle by calling <see cref="ViveCompositionLayerHelper.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <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 XrCompositionLayerBaseHeader structure is not intended to be directly used, but forms a basis for defining current and future structures containing composition layer information. The XrFrameEndInfo structure contains an array of pointers to these polymorphic header structures.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrCompositionLayerBaseHeader
|
||||||
|
{
|
||||||
|
/// <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 in which the layer will be kept stable over time.
|
||||||
|
/// </summary>
|
||||||
|
public XrSpace space;
|
||||||
|
};
|
||||||
|
/// <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(XrStructureType in_type, IntPtr in_next, float in_alpha)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
alpha = in_alpha;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/// <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(XrStructureType in_type, IntPtr in_next, XrCompositionLayerFlags in_layerFlags,
|
||||||
|
XrSpace in_space, XrPassthroughHTC in_passthrough, XrPassthroughColorHTC in_color)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
layerFlags = in_layerFlags;
|
||||||
|
space = in_space;
|
||||||
|
passthrough = in_passthrough;
|
||||||
|
color = in_color;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <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 XrVector3f[] vertices;
|
||||||
|
/// <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 UInt32[] indices;
|
||||||
|
/// <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;
|
||||||
|
public XrPassthroughMeshTransformInfoHTC(XrStructureType in_type, IntPtr in_next, UInt32 in_vertexCount,
|
||||||
|
XrVector3f[] in_vertices, UInt32 in_indexCount, UInt32[] in_indices, XrSpace in_baseSpace, XrTime in_time,
|
||||||
|
XrPosef in_pose, XrVector3f in_scale)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
vertexCount = in_vertexCount;
|
||||||
|
vertices = in_vertices;
|
||||||
|
indexCount = in_indexCount;
|
||||||
|
indices = in_indices;
|
||||||
|
baseSpace = in_baseSpace;
|
||||||
|
time = in_time;
|
||||||
|
pose = in_pose;
|
||||||
|
scale = in_scale;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static class ViveCompositionLayerHelper
|
public static class ViveCompositionLayerHelper
|
||||||
{
|
{
|
||||||
|
/// <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);
|
||||||
// Flag bits for XrCompositionLayerFlags
|
// Flag bits for XrCompositionLayerFlags
|
||||||
public static XrCompositionLayerFlags XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001;
|
public static XrCompositionLayerFlags XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001;
|
||||||
public static XrCompositionLayerFlags XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002;
|
public static XrCompositionLayerFlags XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
DocumentationLink = "..\\Documentation",
|
DocumentationLink = "..\\Documentation",
|
||||||
OpenxrExtensionStrings = kOpenxrExtensionStrings,
|
OpenxrExtensionStrings = kOpenxrExtensionStrings,
|
||||||
Version = "1.0.0",
|
Version = "1.0.0",
|
||||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
BuildTargetGroups = new[] { BuildTargetGroup.Android ,BuildTargetGroup.Standalone},
|
||||||
FeatureId = featureId
|
FeatureId = featureId
|
||||||
)]
|
)]
|
||||||
#endif
|
#endif
|
||||||
@@ -36,6 +36,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||||
|
|
||||||
private List<int> passthroughIDList = new List<int>();
|
private List<int> passthroughIDList = new List<int>();
|
||||||
|
/// <summary>
|
||||||
|
/// The List of passthrough ID.
|
||||||
|
/// </summary>
|
||||||
public List<int> PassthroughIDList { get{ return new List<int>(passthroughIDList); } }
|
public List<int> PassthroughIDList { get{ return new List<int>(passthroughIDList); } }
|
||||||
|
|
||||||
private List<XRInputSubsystem> inputSubsystems = new List<XRInputSubsystem>();
|
private List<XRInputSubsystem> inputSubsystems = new List<XRInputSubsystem>();
|
||||||
@@ -45,9 +48,15 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string featureId = "vive.openxr.feature.compositionlayer.passthrough";
|
public const string featureId = "vive.openxr.feature.compositionlayer.passthrough";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The extension string.
|
||||||
|
/// </summary>
|
||||||
public const string kOpenxrExtensionStrings = "XR_HTC_passthrough";
|
public const string kOpenxrExtensionStrings = "XR_HTC_passthrough";
|
||||||
|
|
||||||
private bool m_HTCPassthroughExtensionEnabled = true;
|
private bool m_HTCPassthroughExtensionEnabled = true;
|
||||||
|
/// <summary>
|
||||||
|
/// The HTC Passthrough extension is enabled or not.
|
||||||
|
/// </summary>
|
||||||
public bool HTCPassthroughExtensionEnabled
|
public bool HTCPassthroughExtensionEnabled
|
||||||
{
|
{
|
||||||
get { return m_HTCPassthroughExtensionEnabled; }
|
get { return m_HTCPassthroughExtensionEnabled; }
|
||||||
@@ -55,10 +64,118 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
#region OpenXR Life Cycle
|
#region OpenXR Life Cycle
|
||||||
private bool m_XrInstanceCreated = false;
|
private bool m_XrInstanceCreated = false;
|
||||||
|
/// <summary>
|
||||||
|
/// The XR instance is created or not.
|
||||||
|
/// </summary>
|
||||||
public bool XrInstanceCreated
|
public bool XrInstanceCreated
|
||||||
{
|
{
|
||||||
get { return m_XrInstanceCreated; }
|
get { return m_XrInstanceCreated; }
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
private static IntPtr xrGetInstanceProcAddr_prev;
|
||||||
|
private static IntPtr XrEndFrame_prev;
|
||||||
|
private static IntPtr XrWaitFrame_prev;
|
||||||
|
private static List<IntPtr> layerListOrigin = new List<IntPtr>();
|
||||||
|
private static List<IntPtr> layerListModified = new List<IntPtr>();
|
||||||
|
private static IntPtr layersModified = Marshal.AllocHGlobal((int)(Marshal.SizeOf(typeof(IntPtr)) * 30)); //Preallocate a layer buffer with sufficient size and reuse it for each frame.
|
||||||
|
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log("EXT: registering our own xrGetInstanceProcAddr");
|
||||||
|
xrGetInstanceProcAddr_prev = func;
|
||||||
|
return Marshal.GetFunctionPointerForDelegate(Intercept_xrGetInstanceProcAddr);
|
||||||
|
}
|
||||||
|
[MonoPInvokeCallback(typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate))]
|
||||||
|
private static XrResult InterceptXrEndFrame_xrGetInstanceProcAddr(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
if (xrGetInstanceProcAddr_prev == null || xrGetInstanceProcAddr_prev == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogError("xrGetInstanceProcAddr_prev is null");
|
||||||
|
function = IntPtr.Zero;
|
||||||
|
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get delegate of old xrGetInstanceProcAddr.
|
||||||
|
var xrGetProc = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrGetInstanceProcAddrDelegate>(xrGetInstanceProcAddr_prev);
|
||||||
|
XrResult result = xrGetProc(instance, name, out function);
|
||||||
|
if (name == "xrEndFrame")
|
||||||
|
{
|
||||||
|
XrEndFrame_prev = function;
|
||||||
|
m_intercept_xrEndFrame = intercepted_xrEndFrame;
|
||||||
|
function = Marshal.GetFunctionPointerForDelegate(m_intercept_xrEndFrame); ;
|
||||||
|
UnityEngine.Debug.Log("Getting xrEndFrame func");
|
||||||
|
}
|
||||||
|
if (name == "xrWaitFrame")
|
||||||
|
{
|
||||||
|
XrWaitFrame_prev = function;
|
||||||
|
m_intercept_xrWaitFrame = intercepted_xrWaitFrame;
|
||||||
|
function = Marshal.GetFunctionPointerForDelegate(m_intercept_xrWaitFrame); ;
|
||||||
|
UnityEngine.Debug.Log("Getting xrWaitFrame func");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MonoPInvokeCallback(typeof(OpenXRHelper.xrEndFrameDelegate))]
|
||||||
|
private static XrResult intercepted_xrEndFrame(XrSession session, ref XrFrameEndInfo frameEndInfo)
|
||||||
|
{
|
||||||
|
XrResult res;
|
||||||
|
// Get delegate of prev xrEndFrame.
|
||||||
|
var xrEndFrame = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrEndFrameDelegate>(XrEndFrame_prev);
|
||||||
|
|
||||||
|
layerListOrigin.Clear();
|
||||||
|
uint layerCount = frameEndInfo.layerCount;
|
||||||
|
IntPtr layers = frameEndInfo.layers;
|
||||||
|
for (int i = 0; i < layerCount; i++)
|
||||||
|
{
|
||||||
|
IntPtr ptr = Marshal.ReadIntPtr(layers, i * Marshal.SizeOf(typeof(IntPtr)));
|
||||||
|
XrCompositionLayerBaseHeader header = (XrCompositionLayerBaseHeader)Marshal.PtrToStructure(ptr, typeof(XrCompositionLayerBaseHeader));
|
||||||
|
layerListOrigin.Add(ptr);
|
||||||
|
}
|
||||||
|
List<IntPtr> layerListNew;
|
||||||
|
if (layerListModified.Count != 0)
|
||||||
|
{
|
||||||
|
layerListNew = new List<IntPtr>(layerListModified);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
layerListNew = new List<IntPtr>(layerListOrigin);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < layerListNew.Count; i++)
|
||||||
|
{
|
||||||
|
Marshal.WriteIntPtr(layersModified, i * Marshal.SizeOf(typeof(IntPtr)), layerListNew[i]);
|
||||||
|
}
|
||||||
|
frameEndInfo.layers = layersModified;
|
||||||
|
frameEndInfo.layerCount = (uint)layerListNew.Count;
|
||||||
|
|
||||||
|
res = xrEndFrame(session, ref frameEndInfo);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private static XrFrameWaitInfo m_frameWaitInfo;
|
||||||
|
private static XrFrameState m_frameState;
|
||||||
|
[MonoPInvokeCallback(typeof(OpenXRHelper.xrWaitFrameDelegate))]
|
||||||
|
private static int intercepted_xrWaitFrame(ulong session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState)
|
||||||
|
{
|
||||||
|
var xrWaitFrame = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrWaitFrameDelegate>(XrWaitFrame_prev);
|
||||||
|
int res = xrWaitFrame(session, ref frameWaitInfo, ref frameState);
|
||||||
|
m_frameWaitInfo = frameWaitInfo;
|
||||||
|
m_frameState = frameState;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public void GetOriginEndFrameLayerList(out List<IntPtr> layers)
|
||||||
|
{
|
||||||
|
layers = new List<IntPtr>(layerListOrigin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SubmitLayers(List<IntPtr> layers)
|
||||||
|
{
|
||||||
|
layerListModified = new List<IntPtr>(layers);
|
||||||
|
//UnityEngine.Debug.Log("####Update submit end " + layerListModified.Count);
|
||||||
|
}
|
||||||
|
public XrFrameState GetFrameState()
|
||||||
|
{
|
||||||
|
return m_frameState;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private XrInstance m_XrInstance = 0;
|
private XrInstance m_XrInstance = 0;
|
||||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||||
{
|
{
|
||||||
@@ -94,6 +211,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
}
|
}
|
||||||
|
|
||||||
private bool m_XrSessionCreated = false;
|
private bool m_XrSessionCreated = false;
|
||||||
|
/// <summary>
|
||||||
|
/// The XR session is created or not.
|
||||||
|
/// </summary>
|
||||||
public bool XrSessionCreated
|
public bool XrSessionCreated
|
||||||
{
|
{
|
||||||
get { return m_XrSessionCreated; }
|
get { return m_XrSessionCreated; }
|
||||||
@@ -107,6 +227,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
}
|
}
|
||||||
|
|
||||||
private bool m_XrSessionEnding = false;
|
private bool m_XrSessionEnding = false;
|
||||||
|
/// <summary>
|
||||||
|
/// The XR session is ending or not.
|
||||||
|
/// </summary>
|
||||||
public bool XrSessionEnding
|
public bool XrSessionEnding
|
||||||
{
|
{
|
||||||
get { return m_XrSessionEnding; }
|
get { return m_XrSessionEnding; }
|
||||||
@@ -239,13 +362,16 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
DEBUG("OnSessionEnd() " + m_XrSession);
|
DEBUG("OnSessionEnd() " + m_XrSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The delegate of Passthrough Session Destroy.
|
||||||
|
/// </summary>
|
||||||
public delegate void OnPassthroughSessionDestroyDelegate(int passthroughID);
|
public delegate void OnPassthroughSessionDestroyDelegate(int passthroughID);
|
||||||
private Dictionary<int, OnPassthroughSessionDestroyDelegate> OnPassthroughSessionDestroyHandlerDictionary = new Dictionary<int, OnPassthroughSessionDestroyDelegate>();
|
private Dictionary<int, OnPassthroughSessionDestroyDelegate> OnPassthroughSessionDestroyHandlerDictionary = new Dictionary<int, OnPassthroughSessionDestroyDelegate>();
|
||||||
protected override void OnSessionDestroy(ulong xrSession)
|
protected override void OnSessionDestroy(ulong xrSession)
|
||||||
{
|
{
|
||||||
m_XrSessionCreated = false;
|
m_XrSessionCreated = false;
|
||||||
DEBUG("OnSessionDestroy() " + xrSession);
|
DEBUG("OnSessionDestroy() " + xrSession);
|
||||||
|
#if UNITY_ANDROID
|
||||||
//Notify that all passthrough layers should be destroyed
|
//Notify that all passthrough layers should be destroyed
|
||||||
List<int> currentPassthroughIDs = PassthroughIDList;
|
List<int> currentPassthroughIDs = PassthroughIDList;
|
||||||
foreach (int passthroughID in currentPassthroughIDs)
|
foreach (int passthroughID in currentPassthroughIDs)
|
||||||
@@ -254,7 +380,15 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
OnPassthroughSessionDestroyHandler?.Invoke(passthroughID);
|
OnPassthroughSessionDestroyHandler?.Invoke(passthroughID);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
//Notify that all passthrough layers should be destroyed
|
||||||
|
List<XrPassthroughHTC> currentPassthroughs = PassthroughList;
|
||||||
|
foreach (XrPassthroughHTC passthrough in currentPassthroughs)
|
||||||
|
{
|
||||||
|
DestroyPassthroughHTC(passthrough);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (m_HeadLockSpace != 0)
|
if (m_HeadLockSpace != 0)
|
||||||
{
|
{
|
||||||
DestroySpace(m_HeadLockSpace);
|
DestroySpace(m_HeadLockSpace);
|
||||||
@@ -272,6 +406,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current XR session state.
|
||||||
|
/// </summary>
|
||||||
public XrSessionState XrSessionCurrentState
|
public XrSessionState XrSessionCurrentState
|
||||||
{
|
{
|
||||||
get { return m_XrSessionNewState; }
|
get { return m_XrSessionNewState; }
|
||||||
@@ -301,14 +438,23 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region OpenXR function delegates
|
#region OpenXR function delegates
|
||||||
/// xrGetInstanceProcAddr
|
/// xrGetInstanceProcAddr
|
||||||
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
OpenXRHelper.xrGetInstanceProcAddrDelegate Intercept_xrGetInstanceProcAddr =
|
||||||
|
new OpenXRHelper.xrGetInstanceProcAddrDelegate(InterceptXrEndFrame_xrGetInstanceProcAddr);
|
||||||
|
#endif
|
||||||
|
private static OpenXRHelper.xrEndFrameDelegate m_intercept_xrEndFrame;
|
||||||
|
private static OpenXRHelper.xrWaitFrameDelegate m_intercept_xrWaitFrame;
|
||||||
/// xrGetSystemProperties
|
/// xrGetSystemProperties
|
||||||
OpenXRHelper.xrGetSystemPropertiesDelegate xrGetSystemProperties;
|
OpenXRHelper.xrGetSystemPropertiesDelegate xrGetSystemProperties;
|
||||||
|
/// <summary>
|
||||||
|
/// Helper function to get this feature' properties.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||||
|
/// </summary>
|
||||||
public XrResult GetSystemProperties(ref XrSystemProperties properties)
|
public XrResult GetSystemProperties(ref XrSystemProperties properties)
|
||||||
{
|
{
|
||||||
if (m_XrInstanceCreated)
|
if (m_XrInstanceCreated)
|
||||||
@@ -335,6 +481,10 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
/// xrCreateReferenceSpace
|
/// xrCreateReferenceSpace
|
||||||
OpenXRHelper.xrCreateReferenceSpaceDelegate xrCreateReferenceSpace;
|
OpenXRHelper.xrCreateReferenceSpaceDelegate xrCreateReferenceSpace;
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a reference space
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrCreateReferenceSpace.html">xrCreateReferenceSpace</see>
|
||||||
|
/// </summary>
|
||||||
public XrResult CreateReferenceSpace(ref XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
public XrResult CreateReferenceSpace(ref XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
||||||
{
|
{
|
||||||
if (!m_XrSessionCreated)
|
if (!m_XrSessionCreated)
|
||||||
@@ -356,7 +506,59 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
}
|
}
|
||||||
return XrResult.XR_ERROR_REFERENCE_SPACE_UNSUPPORTED;
|
return XrResult.XR_ERROR_REFERENCE_SPACE_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
private List<XrPassthroughHTC> passthroughList = new List<XrPassthroughHTC>();
|
||||||
|
public List<XrPassthroughHTC> PassthroughList { get { return new List<XrPassthroughHTC>(passthroughList); } }
|
||||||
|
ViveCompositionLayerHelper.xrCreatePassthroughHTCDelegate xrCreatePassthroughHTC;
|
||||||
|
public XrResult CreatePassthroughHTC(XrPassthroughCreateInfoHTC createInfo, out XrPassthroughHTC passthrough)
|
||||||
|
{
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
ERROR("CreatePassthroughHTC() XR_ERROR_SESSION_LOST.");
|
||||||
|
passthrough = 0;
|
||||||
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
}
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
ERROR("CreatePassthroughHTC() XR_ERROR_INSTANCE_LOST.");
|
||||||
|
passthrough = 0;
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
XrResult res = xrCreatePassthroughHTC(m_XrSession, createInfo, out passthrough);
|
||||||
|
if (res == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
passthroughList.Add(passthrough);
|
||||||
|
passthroughIDList.Add(((int)(ulong)passthrough));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ERROR("CreatePassthroughHTC() "+res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViveCompositionLayerHelper.xrDestroyPassthroughHTCDelegate xrDestroyPassthroughHTC;
|
||||||
|
public XrResult DestroyPassthroughHTC(XrPassthroughHTC passthrough)
|
||||||
|
{
|
||||||
|
DEBUG("Entry");
|
||||||
|
XrResult res = xrDestroyPassthroughHTC(passthrough);
|
||||||
|
if (res == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
passthroughList.Remove(passthrough);
|
||||||
|
passthroughIDList.Remove(((int)(ulong)passthrough));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// According to XRInputSubsystem's tracking origin mode, return the corresponding XrSpace.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrSpace GetTrackingSpace()
|
||||||
|
{
|
||||||
|
var s = GetCurrentAppSpace();
|
||||||
|
Debug.Log("VivePassthrough GetTrackingSpace() s=" + s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||||
{
|
{
|
||||||
/// xrGetInstanceProcAddr
|
/// xrGetInstanceProcAddr
|
||||||
@@ -438,7 +640,41 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
ERROR("xrDestroySpace");
|
ERROR("xrDestroySpace");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
/// xrCreatePassthroughHTC
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrCreatePassthroughHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrCreatePassthroughHTC.");
|
||||||
|
xrCreatePassthroughHTC = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(ViveCompositionLayerHelper.xrCreatePassthroughHTCDelegate)) as ViveCompositionLayerHelper.xrCreatePassthroughHTCDelegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrCreatePassthroughHTC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/// xrCreatePassthroughHTC
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrDestroyPassthroughHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrDestroyPassthroughHTC.");
|
||||||
|
xrDestroyPassthroughHTC = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(ViveCompositionLayerHelper.xrDestroyPassthroughHTCDelegate)) as ViveCompositionLayerHelper.xrDestroyPassthroughHTCDelegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrDestroyPassthroughHTC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if UNITY_ANDROID
|
||||||
if (HTCPassthrough_GetFuncAddrs(xrInstance, xrGetInstanceProcAddr) == XrResult.XR_SUCCESS)
|
if (HTCPassthrough_GetFuncAddrs(xrInstance, xrGetInstanceProcAddr) == XrResult.XR_SUCCESS)
|
||||||
{
|
{
|
||||||
DEBUG("Get function pointers in native.");
|
DEBUG("Get function pointers in native.");
|
||||||
@@ -448,15 +684,18 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
ERROR("HTCPassthrough_GetFuncAddrs");
|
ERROR("HTCPassthrough_GetFuncAddrs");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Wrapper Functions
|
#region Wrapper Functions
|
||||||
private const string ExtLib = "viveopenxr";
|
private const string ExtLib = "viveopenxr";
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_CreatePassthrough")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_CreatePassthrough")]
|
||||||
private static extern int VIVEOpenXR_HTCPassthrough_CreatePassthrough(XrSession session, LayerType layerType, PassthroughLayerForm layerForm, uint compositionDepth = 0);
|
private static extern int VIVEOpenXR_HTCPassthrough_CreatePassthrough(XrSession session, LayerType layerType, PassthroughLayerForm layerForm, uint compositionDepth = 0);
|
||||||
|
/// <summary>
|
||||||
|
/// Create Passthrough.
|
||||||
|
/// </summary>
|
||||||
public int HTCPassthrough_CreatePassthrough(LayerType layerType, PassthroughLayerForm layerForm, OnPassthroughSessionDestroyDelegate onDestroyPassthroughHandler, uint compositionDepth = 0)
|
public int HTCPassthrough_CreatePassthrough(LayerType layerType, PassthroughLayerForm layerForm, OnPassthroughSessionDestroyDelegate onDestroyPassthroughHandler, uint compositionDepth = 0)
|
||||||
{
|
{
|
||||||
if (!m_XrSessionCreated || m_XrSession == 0)
|
if (!m_XrSessionCreated || m_XrSession == 0)
|
||||||
@@ -484,6 +723,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetAlpha")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetAlpha")]
|
||||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetAlpha(int passthroughID, float alpha);
|
private static extern bool VIVEOpenXR_HTCPassthrough_SetAlpha(int passthroughID, float alpha);
|
||||||
|
/// <summary>
|
||||||
|
/// Set Passthough Alpha.
|
||||||
|
/// </summary>
|
||||||
public bool HTCPassthrough_SetAlpha(int passthroughID, float alpha)
|
public bool HTCPassthrough_SetAlpha(int passthroughID, float alpha)
|
||||||
{
|
{
|
||||||
if (!HTCPassthroughExtensionEnabled)
|
if (!HTCPassthroughExtensionEnabled)
|
||||||
@@ -497,6 +739,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetLayerType")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetLayerType")]
|
||||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetLayerType(int passthroughID, LayerType layerType, uint compositionDepth = 0);
|
private static extern bool VIVEOpenXR_HTCPassthrough_SetLayerType(int passthroughID, LayerType layerType, uint compositionDepth = 0);
|
||||||
|
/// <summary>
|
||||||
|
/// Set Passthough Layer Type.
|
||||||
|
/// </summary>
|
||||||
public bool HTCPassthrough_SetLayerType(int passthroughID, LayerType layerType, uint compositionDepth = 0)
|
public bool HTCPassthrough_SetLayerType(int passthroughID, LayerType layerType, uint compositionDepth = 0)
|
||||||
{
|
{
|
||||||
if (!HTCPassthroughExtensionEnabled)
|
if (!HTCPassthroughExtensionEnabled)
|
||||||
@@ -510,6 +755,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMesh")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMesh")]
|
||||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMesh(int passthroughID, uint vertexCount, [In, Out] XrVector3f[] vertexBuffer, uint indexCount, [In, Out] uint[] indexBuffer);
|
private static extern bool VIVEOpenXR_HTCPassthrough_SetMesh(int passthroughID, uint vertexCount, [In, Out] XrVector3f[] vertexBuffer, uint indexCount, [In, Out] uint[] indexBuffer);
|
||||||
|
/// <summary>
|
||||||
|
/// Set Passthough Mesh.
|
||||||
|
/// </summary>
|
||||||
public bool HTCPassthrough_SetMesh(int passthroughID, uint vertexCount, [In, Out] XrVector3f[] vertexBuffer, uint indexCount, [In, Out] uint[] indexBuffer)
|
public bool HTCPassthrough_SetMesh(int passthroughID, uint vertexCount, [In, Out] XrVector3f[] vertexBuffer, uint indexCount, [In, Out] uint[] indexBuffer)
|
||||||
{
|
{
|
||||||
if (!HTCPassthroughExtensionEnabled)
|
if (!HTCPassthroughExtensionEnabled)
|
||||||
@@ -523,6 +771,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransform")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransform")]
|
||||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransform(int passthroughID, XrSpace meshSpace, XrPosef meshPose, XrVector3f meshScale);
|
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransform(int passthroughID, XrSpace meshSpace, XrPosef meshPose, XrVector3f meshScale);
|
||||||
|
/// <summary>
|
||||||
|
/// Set Passthough Mesh Transform.
|
||||||
|
/// </summary>
|
||||||
public bool HTCPassthrough_SetMeshTransform(int passthroughID, XrSpace meshSpace, XrPosef meshPose, XrVector3f meshScale)
|
public bool HTCPassthrough_SetMeshTransform(int passthroughID, XrSpace meshSpace, XrPosef meshPose, XrVector3f meshScale)
|
||||||
{
|
{
|
||||||
if (!HTCPassthroughExtensionEnabled)
|
if (!HTCPassthroughExtensionEnabled)
|
||||||
@@ -536,6 +787,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformSpace")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformSpace")]
|
||||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformSpace(int passthroughID, XrSpace meshSpace);
|
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformSpace(int passthroughID, XrSpace meshSpace);
|
||||||
|
/// <summary>
|
||||||
|
/// Set Passthough Mesh Transform Space.
|
||||||
|
/// </summary>
|
||||||
public bool HTCPassthrough_SetMeshTransformSpace(int passthroughID, XrSpace meshSpace)
|
public bool HTCPassthrough_SetMeshTransformSpace(int passthroughID, XrSpace meshSpace)
|
||||||
{
|
{
|
||||||
if (!HTCPassthroughExtensionEnabled)
|
if (!HTCPassthroughExtensionEnabled)
|
||||||
@@ -549,6 +803,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformPosition")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformPosition")]
|
||||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformPosition(int passthroughID, XrVector3f meshPosition);
|
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformPosition(int passthroughID, XrVector3f meshPosition);
|
||||||
|
/// <summary>
|
||||||
|
/// Set Passthough Mesh Transform Position.
|
||||||
|
/// </summary>
|
||||||
public bool HTCPassthrough_SetMeshTransformPosition(int passthroughID, XrVector3f meshPosition)
|
public bool HTCPassthrough_SetMeshTransformPosition(int passthroughID, XrVector3f meshPosition)
|
||||||
{
|
{
|
||||||
if (!HTCPassthroughExtensionEnabled)
|
if (!HTCPassthroughExtensionEnabled)
|
||||||
@@ -562,6 +819,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformOrientation")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformOrientation")]
|
||||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformOrientation(int passthroughID, XrQuaternionf meshOrientation);
|
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformOrientation(int passthroughID, XrQuaternionf meshOrientation);
|
||||||
|
/// <summary>
|
||||||
|
/// Set Passthough Mesh Transform orientation.
|
||||||
|
/// </summary>
|
||||||
public bool HTCPassthrough_SetMeshTransformOrientation(int passthroughID, XrQuaternionf meshOrientation)
|
public bool HTCPassthrough_SetMeshTransformOrientation(int passthroughID, XrQuaternionf meshOrientation)
|
||||||
{
|
{
|
||||||
if (!HTCPassthroughExtensionEnabled)
|
if (!HTCPassthroughExtensionEnabled)
|
||||||
@@ -575,6 +835,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformScale")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformScale")]
|
||||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformScale(int passthroughID, XrVector3f meshScale);
|
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformScale(int passthroughID, XrVector3f meshScale);
|
||||||
|
/// <summary>
|
||||||
|
/// Set Passthough Mesh Transform scale.
|
||||||
|
/// </summary>
|
||||||
public bool HTCPassthrough_SetMeshTransformScale(int passthroughID, XrVector3f meshScale)
|
public bool HTCPassthrough_SetMeshTransformScale(int passthroughID, XrVector3f meshScale)
|
||||||
{
|
{
|
||||||
if (!HTCPassthroughExtensionEnabled)
|
if (!HTCPassthroughExtensionEnabled)
|
||||||
@@ -588,6 +851,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_DestroyPassthrough")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_DestroyPassthrough")]
|
||||||
private static extern bool VIVEOpenXR_HTCPassthrough_DestroyPassthrough(int passthroughID);
|
private static extern bool VIVEOpenXR_HTCPassthrough_DestroyPassthrough(int passthroughID);
|
||||||
|
/// <summary>
|
||||||
|
/// Destroy Passthough.
|
||||||
|
/// </summary>
|
||||||
public bool HTCPassthrough_DestroyPassthrough(int passthroughID)
|
public bool HTCPassthrough_DestroyPassthrough(int passthroughID)
|
||||||
{
|
{
|
||||||
if (!HTCPassthroughExtensionEnabled)
|
if (!HTCPassthroughExtensionEnabled)
|
||||||
@@ -606,9 +872,9 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
return destroyed;
|
return destroyed;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Hook native functions
|
#region Hook native functions
|
||||||
|
|
||||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_GetFuncAddrs")]
|
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_GetFuncAddrs")]
|
||||||
private static extern XrResult VIVEOpenXR_HTCPassthrough_GetFuncAddrs(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrFuncPtr);
|
private static extern XrResult VIVEOpenXR_HTCPassthrough_GetFuncAddrs(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrFuncPtr);
|
||||||
@@ -623,10 +889,12 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
return VIVEOpenXR_HTCPassthrough_GetFuncAddrs(xrInstance, xrGetInstanceProcAddrFuncPtr);
|
return VIVEOpenXR_HTCPassthrough_GetFuncAddrs(xrInstance, xrGetInstanceProcAddrFuncPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Helper Funcs
|
|
||||||
|
|
||||||
|
#region Helper Funcs
|
||||||
|
/// <summary>
|
||||||
|
/// Helper function to get XrSpace from space type.
|
||||||
|
/// </summary>
|
||||||
public XrSpace GetXrSpaceFromSpaceType(ProjectedPassthroughSpaceType spaceType)
|
public XrSpace GetXrSpaceFromSpaceType(ProjectedPassthroughSpaceType spaceType)
|
||||||
{
|
{
|
||||||
XrSpace meshSpace = 0;
|
XrSpace meshSpace = 0;
|
||||||
@@ -669,6 +937,6 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
return meshSpace;
|
return meshSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ using UnityEditor.XR.OpenXR.Features;
|
|||||||
namespace VIVE.OpenXR.DisplayRefreshRate
|
namespace VIVE.OpenXR.DisplayRefreshRate
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
[OpenXRFeature(UiName = "XR FB Display Refresh Rate",
|
[OpenXRFeature(UiName = "VIVE XR Display Refresh Rate",
|
||||||
BuildTargetGroups = new[] { BuildTargetGroup.Android},
|
BuildTargetGroups = new[] { BuildTargetGroup.Android},
|
||||||
Company = "HTC",
|
Company = "HTC",
|
||||||
Desc = "Support the display refresh rate.",
|
Desc = "Support the display refresh rate.",
|
||||||
|
|||||||
8
com.htc.upm.vive.openxr/Runtime/Features/EyeTracker.meta
Normal file
8
com.htc.upm.vive.openxr/Runtime/Features/EyeTracker.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 65a24dbb45287c244bce088cb4a0a8aa
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,676 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
using UnityEngine.XR.OpenXR;
|
||||||
|
using UnityEngine.XR.OpenXR.Features;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System;
|
||||||
|
using AOT;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.XR.OpenXR.Features;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.EyeTracker
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[OpenXRFeature(UiName = "VIVE XR Eye Tracker",
|
||||||
|
BuildTargetGroups = new[] { BuildTargetGroup.Standalone },
|
||||||
|
Company = "HTC",
|
||||||
|
Desc = "Support the eye tracker extension.",
|
||||||
|
DocumentationLink = "..\\Documentation",
|
||||||
|
OpenxrExtensionStrings = kOpenxrExtensionString,
|
||||||
|
Version = "1.0.0",
|
||||||
|
FeatureId = featureId)]
|
||||||
|
#endif
|
||||||
|
public class ViveEyeTracker : OpenXRFeature
|
||||||
|
{
|
||||||
|
const string LOG_TAG = "VIVE.OpenXR.Eye.ViveEyeTracker";
|
||||||
|
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||||
|
void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
|
||||||
|
void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||||
|
|
||||||
|
public const string kOpenxrExtensionString = "XR_HTC_eye_tracker";
|
||||||
|
|
||||||
|
/// <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.eye.tracker";
|
||||||
|
|
||||||
|
#region OpenXR Life Cycle
|
||||||
|
private bool m_XrInstanceCreated = false;
|
||||||
|
private XrInstance m_XrInstance = 0;
|
||||||
|
private static IntPtr xrGetInstanceProcAddr_prev;
|
||||||
|
private static IntPtr WaitFrame_prev;
|
||||||
|
private static XrFrameWaitInfo m_frameWaitInfo;
|
||||||
|
private static XrFrameState m_frameState;
|
||||||
|
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log("EXT: registering our own xrGetInstanceProcAddr");
|
||||||
|
xrGetInstanceProcAddr_prev = func;
|
||||||
|
return Marshal.GetFunctionPointerForDelegate(m_intercept_xrWaitFrame_xrGetInstanceProcAddr);
|
||||||
|
}
|
||||||
|
[MonoPInvokeCallback(typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate))]
|
||||||
|
private static XrResult intercept_xrWaitFrame_xrGetInstanceProcAddr(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
if (xrGetInstanceProcAddr_prev == null || xrGetInstanceProcAddr_prev == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogError("xrGetInstanceProcAddr_prev is null");
|
||||||
|
function = IntPtr.Zero;
|
||||||
|
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get delegate of old xrGetInstanceProcAddr.
|
||||||
|
var xrGetProc = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrGetInstanceProcAddrDelegate>(xrGetInstanceProcAddr_prev);
|
||||||
|
XrResult result = xrGetProc(instance, name, out function);
|
||||||
|
if (name == "xrWaitFrame")
|
||||||
|
{
|
||||||
|
WaitFrame_prev = function;
|
||||||
|
m_intercept_xrWaitFrame = intercepted_xrWaitFrame;
|
||||||
|
function = Marshal.GetFunctionPointerForDelegate(m_intercept_xrWaitFrame); ;
|
||||||
|
UnityEngine.Debug.Log("Getting xrWaitFrame func");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
[MonoPInvokeCallback(typeof(OpenXRHelper.xrWaitFrameDelegate))]
|
||||||
|
private static int intercepted_xrWaitFrame(ulong session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState)
|
||||||
|
{
|
||||||
|
// Get delegate of prev xrWaitFrame.
|
||||||
|
var xrWaitFrame = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrWaitFrameDelegate>(WaitFrame_prev);
|
||||||
|
int res = xrWaitFrame(session, ref frameWaitInfo, ref frameState);
|
||||||
|
m_frameWaitInfo = frameWaitInfo;
|
||||||
|
m_frameState = frameState;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateInstance">xrCreateInstance</see> is done.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xrInstance">The created instance.</param>
|
||||||
|
/// <returns>True for valid <see cref="XrInstance">XrInstance</see></returns>
|
||||||
|
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||||
|
{
|
||||||
|
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
||||||
|
{
|
||||||
|
WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_XrInstanceCreated = true;
|
||||||
|
m_XrInstance = xrInstance;
|
||||||
|
DEBUG("OnInstanceCreate() " + m_XrInstance);
|
||||||
|
return GetXrFunctionDelegates(m_XrInstance);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroyInstance">xrDestroyInstance</see> is done.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xrInstance">The instance to destroy.</param>
|
||||||
|
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||||
|
{
|
||||||
|
m_XrInstanceCreated = false;
|
||||||
|
m_XrInstance = 0;
|
||||||
|
DEBUG("OnInstanceDestroy() " + xrInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private XrSystemId m_XrSystemId = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the <see cref="XrSystemId">XrSystemId</see> retrieved by <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystem">xrGetSystem</see> is changed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xrSystem">The system id.</param>
|
||||||
|
protected override void OnSystemChange(ulong xrSystem)
|
||||||
|
{
|
||||||
|
m_XrSystemId = xrSystem;
|
||||||
|
DEBUG("OnSystemChange() " + m_XrSystemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool m_XrSessionCreated = false;
|
||||||
|
private XrSession m_XrSession = 0;
|
||||||
|
private bool hasEyeTracker = false;
|
||||||
|
private XrEyeTrackerHTC m_EyeTracker = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateSession">xrCreateSession</see> is done.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xrSession">The created session ID.</param>
|
||||||
|
protected override void OnSessionCreate(ulong xrSession)
|
||||||
|
{
|
||||||
|
m_XrSession = xrSession;
|
||||||
|
m_XrSessionCreated = true;
|
||||||
|
DEBUG("OnSessionCreate() " + m_XrSession);
|
||||||
|
|
||||||
|
if (CreateEyeTracker()) { DEBUG("OnSessionCreate() m_EyeTracker " + m_EyeTracker); }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroySession">xrDestroySession</see> is done.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xrSession">The session ID to destroy.</param>
|
||||||
|
protected override void OnSessionDestroy(ulong xrSession)
|
||||||
|
{
|
||||||
|
DEBUG("OnSessionDestroy() " + xrSession);
|
||||||
|
|
||||||
|
// Eye Tracking is binding with xrSession so we destroy the trackers when xrSession is destroyed.
|
||||||
|
DestroyEyeTracker();
|
||||||
|
|
||||||
|
m_XrSession = 0;
|
||||||
|
m_XrSessionCreated = false;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region OpenXR function delegates
|
||||||
|
private static readonly OpenXRHelper.xrGetInstanceProcAddrDelegate m_intercept_xrWaitFrame_xrGetInstanceProcAddr
|
||||||
|
= new OpenXRHelper.xrGetInstanceProcAddrDelegate(intercept_xrWaitFrame_xrGetInstanceProcAddr);
|
||||||
|
private static OpenXRHelper.xrWaitFrameDelegate m_intercept_xrWaitFrame;
|
||||||
|
/// xrGetInstanceProcAddr
|
||||||
|
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||||
|
|
||||||
|
/// xrGetSystemProperties
|
||||||
|
OpenXRHelper.xrGetSystemPropertiesDelegate xrGetSystemProperties;
|
||||||
|
private XrResult GetSystemProperties(ref XrSystemProperties properties)
|
||||||
|
{
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
ERROR("GetSystemProperties() XR_ERROR_SESSION_LOST.");
|
||||||
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
}
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
ERROR("GetSystemProperties() XR_ERROR_INSTANCE_LOST.");
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xrGetSystemProperties(m_XrInstance, m_XrSystemId, ref properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xrDestroySpace
|
||||||
|
OpenXRHelper.xrDestroySpaceDelegate xrDestroySpace;
|
||||||
|
private XrResult DestroySpace(XrSpace space)
|
||||||
|
{
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
ERROR("DestroySpace() XR_ERROR_SESSION_LOST.");
|
||||||
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
}
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
ERROR("DestroySpace() XR_ERROR_INSTANCE_LOST.");
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xrDestroySpace(space);
|
||||||
|
}
|
||||||
|
|
||||||
|
ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate xrCreateEyeTrackerHTC;
|
||||||
|
private XrResult CreateEyeTrackerHTC(ref XrEyeTrackerCreateInfoHTC createInfo, out XrEyeTrackerHTC eyeTracker)
|
||||||
|
{
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
ERROR("CreateEyeTrackerHTC() XR_ERROR_SESSION_LOST.");
|
||||||
|
eyeTracker = 0;
|
||||||
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
}
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
ERROR("CreateEyeTrackerHTC() XR_ERROR_INSTANCE_LOST.");
|
||||||
|
eyeTracker = 0;
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xrCreateEyeTrackerHTC(m_XrSession, ref createInfo, out eyeTracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
ViveEyeTrackerHelper.xrDestroyEyeTrackerHTCDelegate xrDestroyEyeTrackerHTC;
|
||||||
|
private XrResult DestroyEyeTrackerHTC(XrEyeTrackerHTC eyeTracker)
|
||||||
|
{
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
ERROR("DestroyEyeTrackerHTC() XR_ERROR_SESSION_LOST.");
|
||||||
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
}
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
ERROR("DestroyEyeTrackerHTC() XR_ERROR_INSTANCE_LOST.");
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xrDestroyEyeTrackerHTC(eyeTracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
ViveEyeTrackerHelper.xrGetEyeGazeDataHTCDelegate xrGetEyeGazeDataHTC;
|
||||||
|
private XrResult GetEyeGazeDataHTC(XrEyeTrackerHTC eyeTracker,ref XrEyeGazeDataInfoHTC gazeInfo, ref XrEyeGazeDataHTC eyeGazes)
|
||||||
|
{
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
ERROR("GetEyeGazeDataHTC() XR_ERROR_SESSION_LOST.");
|
||||||
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
}
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
ERROR("GetEyeGazeDataHTC() XR_ERROR_INSTANCE_LOST.");
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
XrResult res = xrGetEyeGazeDataHTC(eyeTracker,ref gazeInfo,ref eyeGazes);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
ViveEyeTrackerHelper.xrGetEyePupilDataHTCDelegate xrGetEyePupilDataHTC;
|
||||||
|
|
||||||
|
private XrResult GetEyePupilDataHTC(XrEyeTrackerHTC eyeTracker,ref XrEyePupilDataInfoHTC pupilDataInfo,ref XrEyePupilDataHTC pupilData)
|
||||||
|
{
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
ERROR("GetEyePupilData() XR_ERROR_SESSION_LOST.");
|
||||||
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
}
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
ERROR("GetEyePupilData() XR_ERROR_INSTANCE_LOST.");
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
return xrGetEyePupilDataHTC(eyeTracker,ref pupilDataInfo, ref pupilData);
|
||||||
|
}
|
||||||
|
ViveEyeTrackerHelper.xrGetEyeGeometricDataHTC xrGetEyeGeometricDataHTC;
|
||||||
|
private XrResult GetEyeGeometricDataHTC(XrEyeTrackerHTC eyeTracker,
|
||||||
|
ref XrEyeGeometricDataInfoHTC info,
|
||||||
|
ref XrEyeGeometricDataHTC eyeGeometricData)
|
||||||
|
{
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
ERROR("GetEyeGeometricData() XR_ERROR_SESSION_LOST.");
|
||||||
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
}
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
ERROR("GetEyeGeometricData() XR_ERROR_INSTANCE_LOST.");
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
return xrGetEyeGeometricDataHTC(eyeTracker,ref info, ref eyeGeometricData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||||
|
{
|
||||||
|
/// xrGetInstanceProcAddr
|
||||||
|
if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrGetInstanceProcAddr.");
|
||||||
|
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
xrGetInstanceProcAddr,
|
||||||
|
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrGetInstanceProcAddr is null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntPtr funcPtr = IntPtr.Zero;
|
||||||
|
/// xrGetSystemProperties
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrGetSystemProperties", out funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrGetSystemProperties.");
|
||||||
|
xrGetSystemProperties = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(OpenXRHelper.xrGetSystemPropertiesDelegate)) as OpenXRHelper.xrGetSystemPropertiesDelegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrGetSystemProperties");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/// xrDestroySpace
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrDestroySpace", out funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrDestroySpace.");
|
||||||
|
xrDestroySpace = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(OpenXRHelper.xrDestroySpaceDelegate)) as OpenXRHelper.xrDestroySpaceDelegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrDestroySpace");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xrCreateEyeTrackerHTC
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrCreateEyeTrackerHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrCreateEyeTrackerHTC.");
|
||||||
|
xrCreateEyeTrackerHTC = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate)) as ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrCreateEyeTrackerHTC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/// xrDestroyEyeTrackerHTC
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrDestroyEyeTrackerHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrDestroyEyeTrackerHTC.");
|
||||||
|
xrDestroyEyeTrackerHTC = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(ViveEyeTrackerHelper.xrDestroyEyeTrackerHTCDelegate)) as ViveEyeTrackerHelper.xrDestroyEyeTrackerHTCDelegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrDestroyEyeTrackerHTC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/// xrGetEyeGazeDataHTC
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrGetEyeGazeDataHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrGetEyeGazeDataHTC.");
|
||||||
|
xrGetEyeGazeDataHTC = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(ViveEyeTrackerHelper.xrGetEyeGazeDataHTCDelegate)) as ViveEyeTrackerHelper.xrGetEyeGazeDataHTCDelegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrGetEyeGazeDataHTC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/// xrGetEyePupilDataHTC
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrGetEyePupilDataHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrGetEyePupilDataHTC.");
|
||||||
|
xrGetEyePupilDataHTC = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(ViveEyeTrackerHelper.xrGetEyePupilDataHTCDelegate)) as ViveEyeTrackerHelper.xrGetEyePupilDataHTCDelegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrGetEyePupilDataHTC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/// xrGetEyeGeometricDataHTC
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrGetEyeGeometricDataHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrGetEyeGeometricDataHTC.");
|
||||||
|
xrGetEyeGeometricDataHTC = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(ViveEyeTrackerHelper.xrGetEyeGeometricDataHTC)) as ViveEyeTrackerHelper.xrGetEyeGeometricDataHTC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrGetEyeGeometricDataHTC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
XrSystemEyeTrackingPropertiesHTC eyeTrackingSystemProperties;
|
||||||
|
XrSystemProperties systemProperties;
|
||||||
|
private bool IsEyeTrackingSupported()
|
||||||
|
{
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
ERROR("IsEyeTrackingSupported() session is not created.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
eyeTrackingSystemProperties.type = XrStructureType.XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_HTC;
|
||||||
|
systemProperties.type = XrStructureType.XR_TYPE_SYSTEM_PROPERTIES;
|
||||||
|
systemProperties.next = Marshal.AllocHGlobal(Marshal.SizeOf(eyeTrackingSystemProperties));
|
||||||
|
|
||||||
|
long offset = 0;
|
||||||
|
if (IntPtr.Size == 4)
|
||||||
|
offset = systemProperties.next.ToInt32();
|
||||||
|
else
|
||||||
|
offset = systemProperties.next.ToInt64();
|
||||||
|
|
||||||
|
IntPtr sys_eye_tracking_prop_ptr = new IntPtr(offset);
|
||||||
|
Marshal.StructureToPtr(eyeTrackingSystemProperties, sys_eye_tracking_prop_ptr, false);
|
||||||
|
|
||||||
|
if (GetSystemProperties(ref systemProperties) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (IntPtr.Size == 4)
|
||||||
|
offset = systemProperties.next.ToInt32();
|
||||||
|
else
|
||||||
|
offset = systemProperties.next.ToInt64();
|
||||||
|
|
||||||
|
sys_eye_tracking_prop_ptr = new IntPtr(offset);
|
||||||
|
eyeTrackingSystemProperties = (XrSystemEyeTrackingPropertiesHTC)Marshal.PtrToStructure(sys_eye_tracking_prop_ptr, typeof(XrSystemEyeTrackingPropertiesHTC));
|
||||||
|
|
||||||
|
DEBUG("IsEyeTrackingSupported() XrSystemEyeTrackingPropertiesHTC.supportsEyeTracking: "
|
||||||
|
+ eyeTrackingSystemProperties.supportsEyeTracking);
|
||||||
|
|
||||||
|
return (eyeTrackingSystemProperties.supportsEyeTracking > 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("IsEyeTrackingSupported() GetSystemProperties failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An application can create an <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> handle using CreateEyeTracker.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="createInfo">The <see cref="XrEyeTrackerCreateInfoHTC">XrEyeTrackerCreateInfoHTC</see> used to specify the eye tracker.</param>
|
||||||
|
/// <param name="eyeTracker">The returned XrEyeTrackerHTC handle.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public XrResult CreateEyeTracker(XrEyeTrackerCreateInfoHTC createInfo, out XrEyeTrackerHTC eyeTracker)
|
||||||
|
{
|
||||||
|
if (hasEyeTracker)
|
||||||
|
{
|
||||||
|
eyeTracker = m_EyeTracker;
|
||||||
|
DEBUG("CreateEyeTracker() m_EyeTracker: " + eyeTracker + " already created before.");
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsEyeTrackingSupported())
|
||||||
|
{
|
||||||
|
ERROR("CreateEyeTracker() is NOT supported.");
|
||||||
|
eyeTracker = 0;
|
||||||
|
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = CreateEyeTrackerHTC(ref createInfo, out eyeTracker);
|
||||||
|
DEBUG("CreateEyeTracker() " + result + ", eyeTracker: " + eyeTracker);
|
||||||
|
|
||||||
|
if (result == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
hasEyeTracker = true;
|
||||||
|
m_EyeTracker = eyeTracker;
|
||||||
|
DEBUG("CreateEyeTracker() m_EyeTracker " + m_EyeTracker);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// An application can create an <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> handle using CreateEyeTracker.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True for success.</returns>
|
||||||
|
public bool CreateEyeTracker()
|
||||||
|
{
|
||||||
|
XrEyeTrackerCreateInfoHTC createInfo = new XrEyeTrackerCreateInfoHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_EYE_TRACKER_CREATE_INFO_HTC,
|
||||||
|
in_next: IntPtr.Zero);
|
||||||
|
|
||||||
|
var result = CreateEyeTracker(createInfo, out XrEyeTrackerHTC value);
|
||||||
|
DEBUG("CreateEyeTracker() " + " tracker: " + value);
|
||||||
|
return result == XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases the eye tracker and the underlying resources when the eye tracking experience is over.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eyeTracker">An XrEyeTrackerHTC previously created by xrCreateEyeTrackerHTC.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public XrResult DestroyEyeTracker(XrEyeTrackerHTC eyeTracker)
|
||||||
|
{
|
||||||
|
XrResult result = DestroyEyeTrackerHTC(eyeTracker);
|
||||||
|
DEBUG("DestroyEyeTracker() " + eyeTracker + ", result: " + result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Releases the eye tracker and the underlying resources when the eye tracking experience is over.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True for success.</returns>
|
||||||
|
public bool DestroyEyeTracker()
|
||||||
|
{
|
||||||
|
if (!hasEyeTracker)
|
||||||
|
{
|
||||||
|
DEBUG("DestroyEyeTracker() no " + "tracker.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
XrResult ret = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
|
||||||
|
ret = DestroyEyeTracker(m_EyeTracker);
|
||||||
|
hasEyeTracker = false;
|
||||||
|
m_EyeTracker = 0;
|
||||||
|
|
||||||
|
return ret == XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private XrEyeGazeDataHTC m_gazes = new XrEyeGazeDataHTC();// = new XrEyeGazeDataHTC(XrStructureType.XR_TYPE_EYE_GAZE_DATA_HTC, IntPtr.Zero, 0);
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves an array of <see cref="XrSingleEyeGazeDataHTC">XrSingleEyeGazeDataHTC</see> containing the returned eye gaze directions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="out_gazes">Output parameter to retrieve an array of <see cref="XrSingleEyeGazeDataHTC">XrSingleEyeGazeDataHTC</see>.</param>
|
||||||
|
/// <returns>True for success.</returns>
|
||||||
|
public bool GetEyeGazeData(out XrSingleEyeGazeDataHTC[] out_gazes)
|
||||||
|
{
|
||||||
|
m_gazes.type = XrStructureType.XR_TYPE_EYE_GAZE_DATA_HTC;
|
||||||
|
m_gazes.next = IntPtr.Zero;
|
||||||
|
m_gazes.time = m_frameState.predictedDisplayTime;
|
||||||
|
|
||||||
|
out_gazes = m_gazes.gaze;
|
||||||
|
XrEyeGazeDataInfoHTC gazeInfo = new XrEyeGazeDataInfoHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_EYE_GAZE_DATA_INFO_HTC,
|
||||||
|
in_next: IntPtr.Zero,
|
||||||
|
in_baseSpace: GetCurrentAppSpace(),
|
||||||
|
in_time: m_frameState.predictedDisplayTime);
|
||||||
|
if (GetEyeGazeData(m_EyeTracker, gazeInfo, out m_gazes) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
out_gazes = m_gazes.gaze;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="XrEyeGazeDataHTC">XrEyeGazeDataHTC</see> data of a <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="gazeInfo">The information to get eye gaze.</param>
|
||||||
|
/// <param name="eyeGazes">Output parameter to retrieve a pointer to <see cref="XrEyeGazeDataHTC">XrEyeGazeDataHTC</see> receiving the returned eye poses.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public XrResult GetEyeGazeData(XrEyeTrackerHTC eyeTracker, XrEyeGazeDataInfoHTC gazeInfo, out XrEyeGazeDataHTC eyeGazes)
|
||||||
|
{
|
||||||
|
m_gazes.type = XrStructureType.XR_TYPE_EYE_GAZE_DATA_HTC;
|
||||||
|
m_gazes.next = IntPtr.Zero;
|
||||||
|
m_gazes.time = m_frameState.predictedDisplayTime;
|
||||||
|
eyeGazes = m_gazes;
|
||||||
|
XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
result = GetEyeGazeDataHTC(eyeTracker,ref gazeInfo,ref m_gazes);
|
||||||
|
if (result == XrResult.XR_SUCCESS) { eyeGazes = m_gazes; }
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private XrEyePupilDataHTC m_eyePupilData = new XrEyePupilDataHTC();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves an array of <see cref="XrSingleEyePupilDataHTC">XrSingleEyePupilDataHTC</see> containing the returned data for user's pupils.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pupilData">Output parameter to retrieve an array of <see cref="XrSingleEyePupilDataHTC">XrSingleEyePupilDataHTC</see>.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public bool GetEyePupilData(out XrSingleEyePupilDataHTC[] pupilData)
|
||||||
|
{
|
||||||
|
m_eyePupilData.type = XrStructureType.XR_TYPE_EYE_PUPIL_DATA_HTC;
|
||||||
|
m_eyePupilData.next = IntPtr.Zero;
|
||||||
|
m_eyePupilData.time = m_frameState.predictedDisplayTime;
|
||||||
|
pupilData = m_eyePupilData.pupilData;
|
||||||
|
XrEyePupilDataInfoHTC pupilDataInfo = new XrEyePupilDataInfoHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_EYE_PUPIL_DATA_INFO_HTC,
|
||||||
|
in_next: IntPtr.Zero);
|
||||||
|
if (GetEyePupilData(m_EyeTracker, pupilDataInfo, out m_eyePupilData) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
pupilData = m_eyePupilData.pupilData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="XrEyePupilDataHTC">XrEyePupilDataHTC</see> data of a <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="pupilDataInfo">The information to get pupil data.</param>
|
||||||
|
/// <param name="pupilData">A pointer to <see cref="XrEyePupilDataHTC">XrEyePupilDataHTC</see> returned by the runtime.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public XrResult GetEyePupilData(XrEyeTrackerHTC eyeTracker, XrEyePupilDataInfoHTC pupilDataInfo, out XrEyePupilDataHTC pupilData)
|
||||||
|
{
|
||||||
|
m_eyePupilData.type = XrStructureType.XR_TYPE_EYE_PUPIL_DATA_HTC;
|
||||||
|
m_eyePupilData.next = IntPtr.Zero;
|
||||||
|
m_eyePupilData.time = m_frameState.predictedDisplayTime;
|
||||||
|
pupilData = m_eyePupilData;
|
||||||
|
XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
result = GetEyePupilDataHTC(eyeTracker,ref pupilDataInfo, ref m_eyePupilData);
|
||||||
|
if (result == XrResult.XR_SUCCESS) { pupilData = m_eyePupilData; }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private XrEyeGeometricDataHTC m_eyeGeometricData = new XrEyeGeometricDataHTC();//XrStructureType.XR_TYPE_EYE_GEOMETRIC_DATA_HTC, IntPtr.Zero, 0);
|
||||||
|
/// <param name="geometricData">Output parameter to retrieve an array of <see cref="XrSingleEyeGeometricDataHTC">XrSingleEyeGeometricDataHTC</see>.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public bool GetEyeGeometricData(out XrSingleEyeGeometricDataHTC[] geometricData)
|
||||||
|
{
|
||||||
|
m_eyeGeometricData.type = XrStructureType.XR_TYPE_EYE_GEOMETRIC_DATA_HTC;
|
||||||
|
m_eyeGeometricData.next = IntPtr.Zero;
|
||||||
|
m_eyeGeometricData.time = m_frameState.predictedDisplayTime;
|
||||||
|
geometricData = m_eyeGeometricData.eyeGeometricData;
|
||||||
|
XrEyeGeometricDataInfoHTC eyeGeometricDataInfo = new XrEyeGeometricDataInfoHTC(
|
||||||
|
in_type: XrStructureType.XR_TYPE_EYE_GEOMETRIC_DATA_INFO_HTC,
|
||||||
|
in_next: IntPtr.Zero);
|
||||||
|
if (GetEyeGeometricData(m_EyeTracker, eyeGeometricDataInfo, out m_eyeGeometricData) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
geometricData = m_eyeGeometricData.eyeGeometricData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="eyeGeometricDataInfo">A pointer to <see cref="XrEyeGeometricDataInfoHTC">XrEyeGeometricDataInfoHTC</see> structure.</param>
|
||||||
|
/// <param name="eyeGeometricData">A pointer to <see cref="XrEyeGeometricDataHTC">XrEyeGeometricDataHTC</see> returned by the runtime.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public XrResult GetEyeGeometricData(XrEyeTrackerHTC eyeTracker, XrEyeGeometricDataInfoHTC eyeGeometricDataInfo, out XrEyeGeometricDataHTC eyeGeometricData)
|
||||||
|
{
|
||||||
|
m_eyeGeometricData.type = XrStructureType.XR_TYPE_EYE_GEOMETRIC_DATA_HTC;
|
||||||
|
m_eyeGeometricData.next = IntPtr.Zero;
|
||||||
|
m_eyeGeometricData.time = m_frameState.predictedDisplayTime;
|
||||||
|
eyeGeometricData = m_eyeGeometricData;
|
||||||
|
XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
result = GetEyeGeometricDataHTC(eyeTracker,ref eyeGeometricDataInfo, ref m_eyeGeometricData);
|
||||||
|
if (result == XrResult.XR_SUCCESS) { eyeGeometricData = m_eyeGeometricData; }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9eca1674b64bae840af3a53d3ae576ec
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.EyeTracker
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyeTrackerHTC handle represents the resources for eye tracking.
|
||||||
|
/// </summary>
|
||||||
|
public struct XrEyeTrackerHTC : IEquatable<UInt64>
|
||||||
|
{
|
||||||
|
private readonly UInt64 value;
|
||||||
|
|
||||||
|
public XrEyeTrackerHTC(UInt64 u)
|
||||||
|
{
|
||||||
|
value = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator UInt64(XrEyeTrackerHTC equatable)
|
||||||
|
{
|
||||||
|
return equatable.value;
|
||||||
|
}
|
||||||
|
public static implicit operator XrEyeTrackerHTC(UInt64 u)
|
||||||
|
{
|
||||||
|
return new XrEyeTrackerHTC(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(XrEyeTrackerHTC other)
|
||||||
|
{
|
||||||
|
return value == other.value;
|
||||||
|
}
|
||||||
|
public bool Equals(UInt64 other)
|
||||||
|
{
|
||||||
|
return value == other;
|
||||||
|
}
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is XrEyeTrackerHTC && Equals((XrEyeTrackerHTC)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return value.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.Equals(b); }
|
||||||
|
public static bool operator !=(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return !a.Equals(b); }
|
||||||
|
public static bool operator >=(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value >= b.value; }
|
||||||
|
public static bool operator <=(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value <= b.value; }
|
||||||
|
public static bool operator >(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value > b.value; }
|
||||||
|
public static bool operator <(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value < b.value; }
|
||||||
|
public static XrEyeTrackerHTC operator +(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value + b.value; }
|
||||||
|
public static XrEyeTrackerHTC operator -(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value - b.value; }
|
||||||
|
public static XrEyeTrackerHTC operator *(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value * b.value; }
|
||||||
|
public static XrEyeTrackerHTC operator /(XrEyeTrackerHTC a, XrEyeTrackerHTC b)
|
||||||
|
{
|
||||||
|
if (b.value == 0)
|
||||||
|
{
|
||||||
|
throw new DivideByZeroException();
|
||||||
|
}
|
||||||
|
return a.value / b.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyePositionHTC describes which eye is under tracking for the data retrieved from <see cref="XrEyeGazeDataHTC">XrEyeGazeDataHTC</see>, <see cref="XrEyePupilDataHTC">XrEyePupilDataHTC</see> or <see cref="XrEyeGeometricDataHTC">XrEyeGeometricDataHTC</see>.
|
||||||
|
/// </summary>
|
||||||
|
public enum XrEyePositionHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the position of the left eye.
|
||||||
|
/// </summary>
|
||||||
|
XR_EYE_POSITION_LEFT_HTC = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the position of the right eye.
|
||||||
|
/// </summary>
|
||||||
|
XR_EYE_POSITION_RIGHT_HTC = 1,
|
||||||
|
XR_EYE_POSITION_COUNT_HTC = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An application can inspect whether the system is capable of eye tracking input by extending the <see cref="XrSystemProperties">XrSystemProperties</see> with <see cref="XrSystemEyeTrackingPropertiesHTC">XrSystemEyeTrackingPropertiesHTC</see> structure when calling <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrSystemEyeTrackingPropertiesHTC
|
||||||
|
{
|
||||||
|
/// <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>
|
||||||
|
/// Indicating if the current system is capable of receiving eye tracking input.
|
||||||
|
/// </summary>
|
||||||
|
public XrBool32 supportsEyeTracking;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyeTrackerCreateInfoHTC structure describes the information to create an <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> handle.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyeTrackerCreateInfoHTC
|
||||||
|
{
|
||||||
|
/// <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;
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
public XrEyeTrackerCreateInfoHTC(XrStructureType in_type, IntPtr in_next)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyeGazeDataInfoHTC structure describes the information to get eye gaze directions.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyeGazeDataInfoHTC
|
||||||
|
{
|
||||||
|
/// <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>
|
||||||
|
/// An <see cref="XrSpace">XrSpace</see> within which the returned eye poses will be represented.
|
||||||
|
/// </summary>
|
||||||
|
public XrSpace baseSpace;
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrTime">XrTime</see> at which the eye gaze information is requested.
|
||||||
|
/// </summary>
|
||||||
|
public XrTime time;
|
||||||
|
|
||||||
|
/// <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_baseSpace">An <see cref="XrSpace">XrSpace</see> within which the returned eye poses will be represented.</param>
|
||||||
|
/// <param name="in_time">An <see cref="XrTime">XrTime</see> at which the eye gaze information is requested.</param>
|
||||||
|
public XrEyeGazeDataInfoHTC(XrStructureType in_type, IntPtr in_next, XrSpace in_baseSpace, XrTime in_time)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
baseSpace = in_baseSpace;
|
||||||
|
time = in_time;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrSingleEyeGazeDataHTC structure describes the validity and direction of a eye gaze observation.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrSingleEyeGazeDataHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrBool32">XrBool32</see> indicating if the returned gazePose is valid. Callers should check the validity of pose prior to use.
|
||||||
|
/// </summary>
|
||||||
|
public XrBool32 isValid;
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrPosef">XrPosef</see> describing the position and orientation of the user's eye. The pose is represented in the coordinate system provided by <see cref="XrEyeGazeDataInfoHTC">XrEyeGazeDataInfoHTC</see>::<see cref="XrEyeGazeDataInfoHTC.baseSpace">baseSpace</see>.
|
||||||
|
/// </summary>
|
||||||
|
public XrPosef gazePose;
|
||||||
|
|
||||||
|
/// <param name="in_isValid">An <see cref="XrBool32">XrBool32</see> indicating if the returned gazePose is valid. Callers should check the validity of pose prior to use.</param>
|
||||||
|
/// <param name="in_gazePose">An <see cref="XrPosef">XrPosef</see> describing the position and orientation of the user's eye. The pose is represented in the coordinate system provided by <see cref="XrEyeGazeDataInfoHTC">XrEyeGazeDataInfoHTC</see>::<see cref="XrEyeGazeDataInfoHTC.baseSpace">baseSpace</see>.</param>
|
||||||
|
public XrSingleEyeGazeDataHTC(XrBool32 in_isValid, XrPosef in_gazePose)
|
||||||
|
{
|
||||||
|
isValid = in_isValid;
|
||||||
|
gazePose = in_gazePose;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyeGazeDataHTC structure returns the state of the eye gaze directions.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyeGazeDataHTC
|
||||||
|
{
|
||||||
|
/// <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>
|
||||||
|
/// An <see cref="XrTime">XrTime</see> at which the eye gaze information is requested.
|
||||||
|
/// </summary>
|
||||||
|
public XrTime time;
|
||||||
|
/// <summary>
|
||||||
|
/// An array of <see cref="XrSingleEyeGazeDataHTC">XrSingleEyeGazeDataHTC</see> receiving the returned eye gaze directions.
|
||||||
|
/// </summary>
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||||
|
public XrSingleEyeGazeDataHTC[] gaze;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyePupilDataInfoHTC structure describes the information to get pupil data.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyePupilDataInfoHTC
|
||||||
|
{
|
||||||
|
/// <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;
|
||||||
|
/// <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>
|
||||||
|
public XrEyePupilDataInfoHTC(XrStructureType in_type, IntPtr in_next)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrSingleEyePupilDataHTC structure describes the validity, diameter and position of a pupil observation.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrSingleEyePupilDataHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrBool32">XrBool32</see> indicating if the returned pupilDiameter is valid. Callers should check the validity of diameter prior to use.
|
||||||
|
/// </summary>
|
||||||
|
public XrBool32 isDiameterValid;
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrBool32">XrBool32</see> indicating if the returned pupilPosition is valid. Callers should check the validity of position prior to use.
|
||||||
|
/// </summary>
|
||||||
|
public XrBool32 isPositionValid;
|
||||||
|
/// <summary>
|
||||||
|
/// The diameter of pupil in millimeters.
|
||||||
|
/// </summary>
|
||||||
|
public float pupilDiameter;
|
||||||
|
/// <summary>
|
||||||
|
/// The position of pupil in sensor area which x and y are normalized in [0,1] with +Y up and +X to the right.
|
||||||
|
/// </summary>
|
||||||
|
public XrVector2f pupilPosition;
|
||||||
|
|
||||||
|
/// <param name="in_isDiameterValid">An <see cref="XrBool32">XrBool32</see> indicating if the returned gazePose is valid. Callers should check the validity of pose prior to use.</param>
|
||||||
|
/// <param name="in_isPositionValid">An <see cref="XrBool32">XrBool32</see> indicating if the returned pupilPosition is valid. Callers should check the validity of position prior to use.</param>
|
||||||
|
/// <param name="in_pupilDiameter">The diameter of pupil in millimeters.</param>
|
||||||
|
/// <param name="in_pupilPosition">The position of pupil in sensor area which x and y are normalized in [0,1]with +Y up and +X to the right.</param>
|
||||||
|
public XrSingleEyePupilDataHTC(XrBool32 in_isDiameterValid, XrBool32 in_isPositionValid, float in_pupilDiameter, XrVector2f in_pupilPosition)
|
||||||
|
{
|
||||||
|
isDiameterValid = in_isDiameterValid;
|
||||||
|
isPositionValid = in_isPositionValid;
|
||||||
|
pupilDiameter = in_pupilDiameter;
|
||||||
|
pupilPosition = in_pupilPosition;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyePupilDataHTC structure returns the pupil data.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyePupilDataHTC
|
||||||
|
{
|
||||||
|
/// <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>
|
||||||
|
/// An <see cref="XrTime">XrTime</see> at which the pupil data was captured.
|
||||||
|
/// </summary>
|
||||||
|
public XrTime time;
|
||||||
|
/// <summary>
|
||||||
|
/// An array of <see cref="XrSingleEyePupilDataHTC">XrSingleEyePupilDataHTC</see> receiving the returned pupil data.
|
||||||
|
/// </summary>
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||||
|
public XrSingleEyePupilDataHTC[] pupilData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyeGeometricDataInfoHTC structure describes the information to get geometric related data.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyeGeometricDataInfoHTC
|
||||||
|
{
|
||||||
|
/// <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;
|
||||||
|
/// <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>
|
||||||
|
public XrEyeGeometricDataInfoHTC(XrStructureType in_type, IntPtr in_next)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrSingleEyeGeometricDataHTC structure describes the geometric related data.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrSingleEyeGeometricDataHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A flag that indicates if the geometric data is valid. Callers should check the validity of the geometric data prior to use.
|
||||||
|
/// </summary>
|
||||||
|
public XrBool32 isValid;
|
||||||
|
/// <summary>
|
||||||
|
/// A value in range [0,1] representing the openness of the user's eye. When this value is zero, the eye closes normally. When this value is one, the eye opens normally. When this value goes higher, the eye approaches open.
|
||||||
|
/// </summary>
|
||||||
|
public float eyeOpenness;
|
||||||
|
/// <summary>
|
||||||
|
/// A value in range [0,1] representing how the user's eye open widely. When this value is zero, the eye opens normally. When this value goes higher, the eye opens wider.
|
||||||
|
public float eyeWide;
|
||||||
|
/// <summary>
|
||||||
|
/// A value in range [0,1] representing how the user's eye is closed. When this value is zero, the eye closes normally. When this value goes higher, the eye closes tighter.
|
||||||
|
/// </summary>
|
||||||
|
public float eyeSqueeze;
|
||||||
|
|
||||||
|
/// <param name="in_isValid">A flag that indicates if the geometric data is valid. Callers should check the validity of the geometric data prior to use.</param>
|
||||||
|
/// <param name="in_eyeOpenness">A value in range [0,1] representing the openness of the user's eye. When this value is zero, the eye closes normally. When this value is one, the eye opens normally. When this value goes higher, the eye approaches open.</param>
|
||||||
|
/// <param name="in_eyeWide">A value in range [0,1] representing how the user's eye open widely. When this value is zero, the eye opens normally. When this value goes higher, the eye opens wider.</param>
|
||||||
|
/// <param name="in_eyeSqueeze">A value in range [0,1] representing how the user's eye is closed. When this value is zero, the eye closes normally. When this value goes higher, the eye closes tighter.</param>
|
||||||
|
public XrSingleEyeGeometricDataHTC(XrBool32 in_isValid, float in_eyeOpenness, float in_eyeWide, float in_eyeSqueeze)
|
||||||
|
{
|
||||||
|
isValid = in_isValid;
|
||||||
|
eyeOpenness = in_eyeOpenness;
|
||||||
|
eyeWide = in_eyeWide;
|
||||||
|
eyeSqueeze = in_eyeSqueeze;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyeGeometricDataHTC
|
||||||
|
{
|
||||||
|
/// <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>
|
||||||
|
/// An <see cref="XrTime">XrTime</see> at which the returned eye data is tracked.
|
||||||
|
/// </summary>
|
||||||
|
public XrTime time;
|
||||||
|
/// <summary>
|
||||||
|
/// An array of <see cref="XrSingleEyeGeometricDataHTC">XrSingleEyeGeometricDataHTC</see> receiving the returned eye geometric data.
|
||||||
|
/// </summary>
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||||
|
public XrSingleEyeGeometricDataHTC[] eyeGeometricData;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static class ViveEyeTrackerHelper
|
||||||
|
{
|
||||||
|
/// <param name="session">An XrSession in which the eye tracker will be active.</param>
|
||||||
|
/// <param name="createInfo">The <see cref="XrEyeTrackerCreateInfoHTC">XrEyeTrackerCreateInfoHTC</see> used to specify the eye tracker.</param>
|
||||||
|
/// <param name="eyeTracker">The returned <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> handle.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public delegate XrResult xrCreateEyeTrackerHTCDelegate(
|
||||||
|
XrSession session,
|
||||||
|
ref XrEyeTrackerCreateInfoHTC createInfo,
|
||||||
|
out XrEyeTrackerHTC eyeTracker);
|
||||||
|
|
||||||
|
/// <param name="eyeTracker">An XrEyeTrackerHTC previously created by xrCreateEyeTrackerHTC.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public delegate XrResult xrDestroyEyeTrackerHTCDelegate(
|
||||||
|
XrEyeTrackerHTC eyeTracker);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="XrEyeGazeDataHTC">XrEyeGazeDataHTC</see> data of a <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="gazeInfo">The information to get eye gaze.</param>
|
||||||
|
/// <param name="eyeGazes">A pointer to <see cref="XrEyeGazeDataHTC">XrEyeGazeDataHTC</see> receiving the returned eye poses.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public delegate XrResult xrGetEyeGazeDataHTCDelegate(
|
||||||
|
XrEyeTrackerHTC eyeTracker,
|
||||||
|
ref XrEyeGazeDataInfoHTC gazeInfo,
|
||||||
|
ref XrEyeGazeDataHTC eyeGazes);
|
||||||
|
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="pupilDataInfo">The information to get pupil data.</param>
|
||||||
|
/// <param name="pupilData">A pointer to <see cref="XrEyePupilDataHTC">XrEyePupilDataHTC</see> returned by the runtime.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public delegate XrResult xrGetEyePupilDataHTCDelegate(
|
||||||
|
XrEyeTrackerHTC eyeTracker,
|
||||||
|
ref XrEyePupilDataInfoHTC pupilDataInfo,
|
||||||
|
ref XrEyePupilDataHTC pupilData);
|
||||||
|
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="info">A pointer to <see cref="XrEyeGeometricDataInfoHTC">XrEyeGeometricDataInfoHTC</see> structure.</param>
|
||||||
|
/// <param name="eyeGeometricData">A pointer to <see cref="XrEyeGeometricDataHTC">XrEyeGeometricDataHTC</see> returned by the runtime.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public delegate XrResult xrGetEyeGeometricDataHTC(
|
||||||
|
XrEyeTrackerHTC eyeTracker,
|
||||||
|
ref XrEyeGeometricDataInfoHTC info,
|
||||||
|
ref XrEyeGeometricDataHTC eyeGeometricData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e9a5198e29bcef243bf89dc19b46ce0d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -19,7 +19,7 @@ namespace VIVE.OpenXR
|
|||||||
Desc = "Support the HTC foveation extension.",
|
Desc = "Support the HTC foveation extension.",
|
||||||
Company = "HTC",
|
Company = "HTC",
|
||||||
DocumentationLink = "..\\Documentation",
|
DocumentationLink = "..\\Documentation",
|
||||||
OpenxrExtensionStrings = "XR_HTC_foveation",
|
OpenxrExtensionStrings = kOpenxrExtensionString,
|
||||||
Version = "1.0.0",
|
Version = "1.0.0",
|
||||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||||
FeatureId = featureId
|
FeatureId = featureId
|
||||||
@@ -27,6 +27,11 @@ namespace VIVE.OpenXR
|
|||||||
#endif
|
#endif
|
||||||
public class ViveFoveation : OpenXRFeature
|
public class ViveFoveation : OpenXRFeature
|
||||||
{
|
{
|
||||||
|
const string LOG_TAG = "VIVE.OpenXR.ViveFoveation";
|
||||||
|
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||||
|
void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
|
||||||
|
void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flag bits for XrFoveationDynamicFlagsHTC
|
/// Flag bits for XrFoveationDynamicFlagsHTC
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -39,6 +44,31 @@ namespace VIVE.OpenXR
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string featureId = "vive.openxr.feature.foveation";
|
public const string featureId = "vive.openxr.feature.foveation";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OpenXR specification <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_HTC_foveation">12.90. XR_HTC_foveation</see>.
|
||||||
|
/// </summary>
|
||||||
|
public const string kOpenxrExtensionString = "XR_HTC_foveation";
|
||||||
|
|
||||||
|
#region OpenXR Life Cycle
|
||||||
|
/// <summary>
|
||||||
|
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateInstance">xrCreateInstance</see> is done.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xrInstance">The created instance.</param>
|
||||||
|
/// <returns>True for valid <see cref="XrInstance">XrInstance</see></returns>
|
||||||
|
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||||
|
{
|
||||||
|
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
||||||
|
{
|
||||||
|
WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("OnInstanceCreate() " + xrInstance);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
{
|
{
|
||||||
Debug.Log("EXT: registering our own xrGetInstanceProcAddr");
|
Debug.Log("EXT: registering our own xrGetInstanceProcAddr");
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UnityEngine.XR;
|
using UnityEngine.XR;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using AOT;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.XR.OpenXR.Features;
|
using UnityEditor.XR.OpenXR.Features;
|
||||||
@@ -45,6 +45,51 @@ namespace VIVE.OpenXR.Hand
|
|||||||
#region OpenXR Life Cycle
|
#region OpenXR Life Cycle
|
||||||
private bool m_XrInstanceCreated = false;
|
private bool m_XrInstanceCreated = false;
|
||||||
private XrInstance m_XrInstance = 0;
|
private XrInstance m_XrInstance = 0;
|
||||||
|
private static IntPtr xrGetInstanceProcAddr_prev;
|
||||||
|
private static IntPtr WaitFrame_prev;
|
||||||
|
private static XrFrameWaitInfo m_frameWaitInfo;
|
||||||
|
private static XrFrameState m_frameState;
|
||||||
|
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log("EXT: registering our own xrGetInstanceProcAddr");
|
||||||
|
xrGetInstanceProcAddr_prev = func;
|
||||||
|
return Marshal.GetFunctionPointerForDelegate(m_intercept_xrWaitFrame_xrGetInstanceProcAddr);
|
||||||
|
}
|
||||||
|
[MonoPInvokeCallback(typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate))]
|
||||||
|
private static XrResult intercept_xrWaitFrame_xrGetInstanceProcAddr(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
if (xrGetInstanceProcAddr_prev == null || xrGetInstanceProcAddr_prev == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogError("xrGetInstanceProcAddr_prev is null");
|
||||||
|
function = IntPtr.Zero;
|
||||||
|
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get delegate of old xrGetInstanceProcAddr.
|
||||||
|
var xrGetProc = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrGetInstanceProcAddrDelegate>(xrGetInstanceProcAddr_prev);
|
||||||
|
XrResult result = xrGetProc(instance, name, out function);
|
||||||
|
if (name == "xrWaitFrame")
|
||||||
|
{
|
||||||
|
WaitFrame_prev = function;
|
||||||
|
m_intercept_xrWaitFrame = intercepted_xrWaitFrame;
|
||||||
|
function = Marshal.GetFunctionPointerForDelegate(m_intercept_xrWaitFrame); ;
|
||||||
|
UnityEngine.Debug.Log("Getting xrWaitFrame func");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
[MonoPInvokeCallback(typeof(OpenXRHelper.xrWaitFrameDelegate))]
|
||||||
|
private static int intercepted_xrWaitFrame(ulong session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState)
|
||||||
|
{
|
||||||
|
// Get delegate of prev xrWaitFrame.
|
||||||
|
var xrWaitFrame = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrWaitFrameDelegate>(WaitFrame_prev);
|
||||||
|
int res = xrWaitFrame(session, ref frameWaitInfo, ref frameState);
|
||||||
|
m_frameWaitInfo = frameWaitInfo;
|
||||||
|
m_frameState = frameState;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateInstance">xrCreateInstance</see> is done.
|
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateInstance">xrCreateInstance</see> is done.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -276,6 +321,9 @@ namespace VIVE.OpenXR.Hand
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region OpenXR function delegates
|
#region OpenXR function delegates
|
||||||
|
private static readonly OpenXRHelper.xrGetInstanceProcAddrDelegate m_intercept_xrWaitFrame_xrGetInstanceProcAddr
|
||||||
|
= new OpenXRHelper.xrGetInstanceProcAddrDelegate(intercept_xrWaitFrame_xrGetInstanceProcAddr);
|
||||||
|
private static OpenXRHelper.xrWaitFrameDelegate m_intercept_xrWaitFrame;
|
||||||
/// xrGetInstanceProcAddr
|
/// xrGetInstanceProcAddr
|
||||||
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||||
|
|
||||||
@@ -286,8 +334,7 @@ namespace VIVE.OpenXR.Hand
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="properties">Points to an instance of the XrSystemProperties structure, that will be filled with returned information.</param>
|
/// <param name="properties">Points to an instance of the XrSystemProperties structure, that will be filled with returned information.</param>
|
||||||
/// <returns>XR_SUCCESS for success.</returns>
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
[Obsolete("This function will become private in next release")]
|
private XrResult GetSystemProperties(ref XrSystemProperties properties)
|
||||||
public XrResult GetSystemProperties(ref XrSystemProperties properties)
|
|
||||||
{
|
{
|
||||||
if (!m_XrSessionCreated)
|
if (!m_XrSessionCreated)
|
||||||
{
|
{
|
||||||
@@ -312,8 +359,7 @@ namespace VIVE.OpenXR.Hand
|
|||||||
/// <param name="spaceCountOutput">A pointer to the count of spaces written, or a pointer to the required capacity in the case that spaceCapacityInput is insufficient.</param>
|
/// <param name="spaceCountOutput">A pointer to the count of spaces written, or a pointer to the required capacity in the case that spaceCapacityInput is insufficient.</param>
|
||||||
/// <param name="spaces">A pointer to an application-allocated array that will be filled with the enumerant of each supported reference space. It can be NULL if spaceCapacityInput is 0.</param>
|
/// <param name="spaces">A pointer to an application-allocated array that will be filled with the enumerant of each supported reference space. It can be NULL if spaceCapacityInput is 0.</param>
|
||||||
/// <returns>XR_SUCCESS for success.</returns>
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
[Obsolete("This function will become private in next release")]
|
private XrResult EnumerateReferenceSpaces(UInt32 spaceCapacityInput, out UInt32 spaceCountOutput, out XrReferenceSpaceType spaces)
|
||||||
public XrResult EnumerateReferenceSpaces(UInt32 spaceCapacityInput, out UInt32 spaceCountOutput, out XrReferenceSpaceType spaces)
|
|
||||||
{
|
{
|
||||||
if (!m_XrSessionCreated)
|
if (!m_XrSessionCreated)
|
||||||
{
|
{
|
||||||
@@ -341,8 +387,7 @@ namespace VIVE.OpenXR.Hand
|
|||||||
/// <param name="createInfo">The XrReferenceSpaceCreateInfo used to specify the space.</param>
|
/// <param name="createInfo">The XrReferenceSpaceCreateInfo used to specify the space.</param>
|
||||||
/// <param name="space">The returned XrSpace handle.</param>
|
/// <param name="space">The returned XrSpace handle.</param>
|
||||||
/// <returns>XR_SUCCESS for success.</returns>
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
[Obsolete("This function will become private in next release")]
|
private XrResult CreateReferenceSpace(ref XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
||||||
public XrResult CreateReferenceSpace(ref XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
|
||||||
{
|
{
|
||||||
if (!m_XrSessionCreated)
|
if (!m_XrSessionCreated)
|
||||||
{
|
{
|
||||||
@@ -367,8 +412,7 @@ namespace VIVE.OpenXR.Hand
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="space">Must be a valid XrSpace handle.</param>
|
/// <param name="space">Must be a valid XrSpace handle.</param>
|
||||||
/// <returns>XR_SUCCESS for success.</returns>
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
[Obsolete("This function will become private in next release")]
|
private XrResult DestroySpace(XrSpace space)
|
||||||
public XrResult DestroySpace(XrSpace space)
|
|
||||||
{
|
{
|
||||||
if (!m_XrSessionCreated)
|
if (!m_XrSessionCreated)
|
||||||
{
|
{
|
||||||
@@ -713,6 +757,11 @@ namespace VIVE.OpenXR.Hand
|
|||||||
private XrHandJointLocationEXT[] jointLocationsR = new XrHandJointLocationEXT[(int)XrHandJointEXT.XR_HAND_JOINT_MAX_ENUM_EXT];
|
private XrHandJointLocationEXT[] jointLocationsR = new XrHandJointLocationEXT[(int)XrHandJointEXT.XR_HAND_JOINT_MAX_ENUM_EXT];
|
||||||
private XrHandJointLocationsEXT locations = new XrHandJointLocationsEXT(XrStructureType.XR_TYPE_HAND_JOINT_LOCATIONS_EXT, IntPtr.Zero, false, 0, IntPtr.Zero);
|
private XrHandJointLocationsEXT locations = new XrHandJointLocationsEXT(XrStructureType.XR_TYPE_HAND_JOINT_LOCATIONS_EXT, IntPtr.Zero, false, 0, IntPtr.Zero);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="XrSpace"> XrSpace </see> used in Hand Tracking.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="space">Tracking space in <see cref="XrSpace"> XrSpace </see>.</param>
|
||||||
|
/// <returns>True for valid data.</returns>
|
||||||
public bool GetHandTrackingSpace(out XrSpace space)
|
public bool GetHandTrackingSpace(out XrSpace space)
|
||||||
{
|
{
|
||||||
space = 0;
|
space = 0;
|
||||||
@@ -747,7 +796,7 @@ namespace VIVE.OpenXR.Hand
|
|||||||
in_type: XrStructureType.XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT,
|
in_type: XrStructureType.XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT,
|
||||||
in_next: IntPtr.Zero,
|
in_next: IntPtr.Zero,
|
||||||
in_baseSpace: baseSpace,
|
in_baseSpace: baseSpace,
|
||||||
in_time: 1);//
|
in_time: m_frameState.predictedDisplayTime);
|
||||||
|
|
||||||
/// Configures XrHandJointLocationsEXT
|
/// Configures XrHandJointLocationsEXT
|
||||||
locations.type = XrStructureType.XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
|
locations.type = XrStructureType.XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using UnityEngine.XR.OpenXR;
|
|||||||
using UnityEngine.XR.OpenXR.Features;
|
using UnityEngine.XR.OpenXR.Features;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
@@ -14,7 +17,7 @@ namespace VIVE.OpenXR
|
|||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
[OpenXRFeature(UiName = "VIVE XR Path Enumeration",
|
[OpenXRFeature(UiName = "VIVE XR Path Enumeration",
|
||||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone },
|
||||||
Company = "HTC",
|
Company = "HTC",
|
||||||
Desc = "The extension provides more flexibility for the user paths and input/output source paths related to an interaction profile. Developers can use this extension to obtain the path that the user has decided on.",
|
Desc = "The extension provides more flexibility for the user paths and input/output source paths related to an interaction profile. Developers can use this extension to obtain the path that the user has decided on.",
|
||||||
DocumentationLink = "..\\Documentation",
|
DocumentationLink = "..\\Documentation",
|
||||||
@@ -34,7 +37,7 @@ namespace VIVE.OpenXR
|
|||||||
}
|
}
|
||||||
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
|
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
|
||||||
void WARNING(StringBuilder msg) { Debug.LogWarning(msg); }
|
void WARNING(StringBuilder msg) { Debug.LogWarning(msg); }
|
||||||
|
void ERROR(StringBuilder msg) { Debug.LogError(msg); }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OpenXR specification <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_HTC_path_enumeration">12.1. XR_HTC_path_enumeration</see>.
|
/// OpenXR specification <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_HTC_path_enumeration">12.1. XR_HTC_path_enumeration</see>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -66,7 +69,7 @@ namespace VIVE.OpenXR
|
|||||||
m_XrInstanceCreated = true;
|
m_XrInstanceCreated = true;
|
||||||
m_XrInstance = xrInstance;
|
m_XrInstance = xrInstance;
|
||||||
sb.Clear().Append(LOG_TAG).Append("OnInstanceCreate() ").Append(m_XrInstance); DEBUG(sb);
|
sb.Clear().Append(LOG_TAG).Append("OnInstanceCreate() ").Append(m_XrInstance); DEBUG(sb);
|
||||||
|
GetXrFunctionDelegates(m_XrInstance);
|
||||||
return base.OnInstanceCreate(xrInstance);
|
return base.OnInstanceCreate(xrInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,5 +121,171 @@ namespace VIVE.OpenXR
|
|||||||
m_XrSessionCreated = false;
|
m_XrSessionCreated = false;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
#region OpenXR function delegates
|
||||||
|
/// xrGetInstanceProcAddr
|
||||||
|
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||||
|
VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate xrEnumeratePathsForInteractionProfileHTC;
|
||||||
|
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||||
|
{
|
||||||
|
// xrGetInstanceProcAddr
|
||||||
|
if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrGetInstanceProcAddr."); DEBUG(sb);
|
||||||
|
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
xrGetInstanceProcAddr,
|
||||||
|
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrGetInstanceProcAddr"); ERROR(sb);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntPtr funcPtr = IntPtr.Zero;
|
||||||
|
/// xrEnumeratePathsForInteractionProfileHTC
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrEnumeratePathsForInteractionProfileHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrEnumeratePathsForInteractionProfileHTC."); DEBUG(sb);
|
||||||
|
xrEnumeratePathsForInteractionProfileHTC = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate)) as VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC.");
|
||||||
|
ERROR(sb);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC");
|
||||||
|
ERROR(sb);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private List<T> CreateList<T>(UInt32 count, T initialValue)
|
||||||
|
{
|
||||||
|
List<T> list = new List<T>();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
list.Add(initialValue);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XrResult EnumeratePathsForInteractionProfileHTC(
|
||||||
|
ref XrPathsForInteractionProfileEnumerateInfoHTC createInfo,
|
||||||
|
UInt32 pathCapacityInput,
|
||||||
|
ref UInt32 pathCountOutput,
|
||||||
|
[In, Out] XrPath[] paths)
|
||||||
|
{
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
sb.Clear().Append(LOG_TAG).Append("EnumeratePathsForInteractionProfileHTC() XR_ERROR_INSTANCE_LOST."); ERROR(sb);
|
||||||
|
paths = null;
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
return xrEnumeratePathsForInteractionProfileHTC(m_XrInstance,
|
||||||
|
ref createInfo, pathCapacityInput,ref pathCountOutput, paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetUserPaths(string interactionProfileString, out XrPath[] userPaths)
|
||||||
|
{
|
||||||
|
userPaths = null;
|
||||||
|
XrPathsForInteractionProfileEnumerateInfoHTC enumerateInfo;
|
||||||
|
if (!m_XrInstanceCreated) { return false; }
|
||||||
|
|
||||||
|
string func = "GetUserPaths() ";
|
||||||
|
|
||||||
|
if (xrEnumeratePathsForInteractionProfileHTC == null)
|
||||||
|
{
|
||||||
|
sb.Clear().Append(LOG_TAG).Append(func).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC"); WARNING(sb);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Get user path count of sepecified profile.
|
||||||
|
UInt32 trackerCount = 0;
|
||||||
|
enumerateInfo.type = XrStructureType.XR_TYPE_UNKNOWN;
|
||||||
|
enumerateInfo.next = IntPtr.Zero;
|
||||||
|
enumerateInfo.interactionProfile = StringToPath(interactionProfileString);
|
||||||
|
enumerateInfo.userPath = OpenXRHelper.XR_NULL_PATH;
|
||||||
|
|
||||||
|
XrResult result = xrEnumeratePathsForInteractionProfileHTC(m_XrInstance, ref enumerateInfo, 0, ref trackerCount, null);
|
||||||
|
sb.Clear().Append(LOG_TAG).Append(func).Append("xrEnumeratePathsForInteractionProfileHTC result: ").Append(result)
|
||||||
|
.Append(", profile: ").Append(interactionProfileString)
|
||||||
|
.Append(", trackerCount: ").Append(trackerCount);
|
||||||
|
DEBUG(sb);
|
||||||
|
if (result != XrResult.XR_SUCCESS || trackerCount <= 0) { return false; }
|
||||||
|
|
||||||
|
// 2. Get user paths of sepecified profile.
|
||||||
|
List<XrPath> trackerList = CreateList<XrPath>(trackerCount, OpenXRHelper.XR_NULL_PATH);
|
||||||
|
XrPath[] trackers = trackerList.ToArray();
|
||||||
|
result = xrEnumeratePathsForInteractionProfileHTC(
|
||||||
|
m_XrInstance,
|
||||||
|
ref enumerateInfo,
|
||||||
|
pathCapacityInput: (UInt32)(trackers.Length & 0x7FFFFFFF),
|
||||||
|
pathCountOutput: ref trackerCount,
|
||||||
|
paths: trackers);
|
||||||
|
if (result != XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
sb.Clear().Append(LOG_TAG).Append(func).Append("Retrieves trackers failed."); ERROR(sb);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
userPaths = trackers;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetInputPathsWithUserPath(string interactionProfileString, XrPath userPath, out XrPath[] inputPaths)
|
||||||
|
{
|
||||||
|
string func = "GetInputPathsWithUserPath() ";
|
||||||
|
if (!m_XrInstanceCreated) { inputPaths = null; return false; }
|
||||||
|
UInt32 trackerCount = 0;
|
||||||
|
XrPathsForInteractionProfileEnumerateInfoHTC enumerateInfo;
|
||||||
|
enumerateInfo.type = (XrStructureType)1000319000;//Todo : update openxr spec and prevent hard-code.
|
||||||
|
enumerateInfo.next = IntPtr.Zero;
|
||||||
|
enumerateInfo.interactionProfile = StringToPath(interactionProfileString);
|
||||||
|
enumerateInfo.userPath = userPath;
|
||||||
|
UInt32 Count = 0;
|
||||||
|
xrEnumeratePathsForInteractionProfileHTC(
|
||||||
|
m_XrInstance,
|
||||||
|
ref enumerateInfo,
|
||||||
|
0,
|
||||||
|
pathCountOutput: ref Count,
|
||||||
|
paths: null);
|
||||||
|
if (Count > 0)
|
||||||
|
{
|
||||||
|
List<XrPath> pathlist = CreateList<XrPath>(Count, OpenXRHelper.XR_NULL_PATH);
|
||||||
|
inputPaths = pathlist.ToArray();
|
||||||
|
XrResult result = xrEnumeratePathsForInteractionProfileHTC(
|
||||||
|
m_XrInstance,
|
||||||
|
ref enumerateInfo,
|
||||||
|
pathCapacityInput: (UInt32)(inputPaths.Length & 0x7FFFFFFF),
|
||||||
|
pathCountOutput: ref Count,
|
||||||
|
paths: inputPaths);
|
||||||
|
UnityEngine.Debug.Log("Get inputpath str : "+PathToString(inputPaths[0]));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inputPaths = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public string xrPathToString(ulong path)
|
||||||
|
{
|
||||||
|
return PathToString(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong xrStringToPath(string str)
|
||||||
|
{
|
||||||
|
return StringToPath(str);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 33a547f8c3209594c84b8a4a968d8073
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 226c5d25c53e5794baacc000d2eaf274
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9209a4fdd88b4bd4e88afcf05e69cdfd
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: a88ad0ff22dd2484192540d5b8b63ed6
|
guid: 174d574cfa752ab4e9c6354d2a6c14c4
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -0,0 +1,530 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
#define FAKE_DATA
|
||||||
|
#endif
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.XR.OpenXR;
|
||||||
|
using UnityEngine.XR.OpenXR.Features;
|
||||||
|
using VIVE.OpenXR.Feature;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.XR.OpenXR.Features;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.PlaneDetection
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[OpenXRFeature(UiName = "VIVE XR PlaneDetection",
|
||||||
|
Desc = "VIVE's implementaion of the XR_EXT_plane_detection.",
|
||||||
|
Company = "HTC",
|
||||||
|
DocumentationLink = "..\\Documentation",
|
||||||
|
OpenxrExtensionStrings = kOpenxrExtensionString,
|
||||||
|
Version = "1.0.0",
|
||||||
|
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||||
|
FeatureId = featureId
|
||||||
|
)]
|
||||||
|
#endif
|
||||||
|
public class VivePlaneDetection : OpenXRFeature
|
||||||
|
{
|
||||||
|
public const string kOpenxrExtensionString = "XR_EXT_plane_detection";
|
||||||
|
/// <summary>
|
||||||
|
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||||
|
/// </summary>
|
||||||
|
public const string featureId = "vive.wave.openxr.feature.planedetection";
|
||||||
|
private bool m_XrInstanceCreated = false;
|
||||||
|
private XrInstance m_XrInstance = 0;
|
||||||
|
private bool m_XrSessionCreated = false;
|
||||||
|
private XrSession session = 0;
|
||||||
|
private XrSystemId m_XrSystemId = 0;
|
||||||
|
|
||||||
|
|
||||||
|
#region struct, enum, const of this extensions
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorOrientationEXT">XrPlaneDetectorOrientationEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public enum XrPlaneDetectorOrientationEXT
|
||||||
|
{
|
||||||
|
HORIZONTAL_UPWARD_EXT = 0,
|
||||||
|
HORIZONTAL_DOWNWARD_EXT = 1,
|
||||||
|
VERTICAL_EXT = 2,
|
||||||
|
ARBITRARY_EXT = 3,
|
||||||
|
MAX_ENUM_EXT = 0x7FFFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorSemanticTypeEXT">XrPlaneDetectorSemanticTypeEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public enum XrPlaneDetectorSemanticTypeEXT
|
||||||
|
{
|
||||||
|
UNDEFINED_EXT = 0,
|
||||||
|
CEILING_EXT = 1,
|
||||||
|
FLOOR_EXT = 2,
|
||||||
|
WALL_EXT = 3,
|
||||||
|
PLATFORM_EXT = 4,
|
||||||
|
MAX_ENUM_EXT = 0x7FFFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectionStateEXT">XrPlaneDetectionStateEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public enum XrPlaneDetectionStateEXT
|
||||||
|
{
|
||||||
|
NONE_EXT = 0,
|
||||||
|
PENDING_EXT = 1, // Try get plane detection state again
|
||||||
|
DONE_EXT = 2, // Ready to get result
|
||||||
|
ERROR_EXT = 3, // Can try begin again
|
||||||
|
FATAL_EXT = 4, // Should destroy the plane detector
|
||||||
|
MAX_ENUM_EXT = 0x7FFFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
//XrFlags64 XrPlaneDetectionCapabilityFlagsEXT;
|
||||||
|
|
||||||
|
// Flag bits for XrPlaneDetectionCapabilityFlagsEXT
|
||||||
|
public static XrFlags64 CAPABILITY_PLANE_DETECTION_BIT_EXT = 0x00000001;
|
||||||
|
public static XrFlags64 CAPABILITY_PLANE_HOLES_BIT_EXT = 0x00000002;
|
||||||
|
public static XrFlags64 CAPABILITY_SEMANTIC_CEILING_BIT_EXT = 0x00000004;
|
||||||
|
public static XrFlags64 CAPABILITY_SEMANTIC_FLOOR_BIT_EXT = 0x00000008;
|
||||||
|
public static XrFlags64 CAPABILITY_SEMANTIC_WALL_BIT_EXT = 0x00000010;
|
||||||
|
public static XrFlags64 CAPABILITY_SEMANTIC_PLATFORM_BIT_EXT = 0x00000020;
|
||||||
|
public static XrFlags64 CAPABILITY_ORIENTATION_BIT_EXT = 0x00000040;
|
||||||
|
|
||||||
|
//XrFlags64 XrPlaneDetectorFlagsEXT;
|
||||||
|
|
||||||
|
// Flag bits for XrPlaneDetectorFlagsEXT
|
||||||
|
public static XrFlags64 XR_PLANE_DETECTOR_ENABLE_CONTOUR_BIT_EXT = 0x00000001;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrSystemPlaneDetectionPropertiesEXT">XrSystemPlaneDetectionPropertiesEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public struct XrSystemPlaneDetectionPropertiesEXT
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public XrFlags64 supportedFeatures; // XrPlaneDetectionCapabilityFlagsEXT
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorCreateInfoEXT">XrPlaneDetectorCreateInfoEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public struct XrPlaneDetectorCreateInfoEXT
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public XrFlags64 flags; // XrPlaneDetectorFlagsEXT
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrExtent3DfEXT">XrExtent3DfEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public struct XrExtent3DfEXT
|
||||||
|
{
|
||||||
|
public float width;
|
||||||
|
public float height;
|
||||||
|
public float depth;
|
||||||
|
public XrExtent3DfEXT(float width, float height, float depth)
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.depth = depth;
|
||||||
|
}
|
||||||
|
public XrExtent3DfEXT One => new XrExtent3DfEXT(1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorBeginInfoEXT">XrPlaneDetectorBeginInfoEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public struct XrPlaneDetectorBeginInfoEXT
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public XrSpace baseSpace;
|
||||||
|
public XrTime time;
|
||||||
|
public uint orientationCount;
|
||||||
|
public IntPtr orientations; // XrPlaneDetectorOrientationEXT[]
|
||||||
|
public uint semanticTypeCount;
|
||||||
|
public IntPtr semanticTypes; // XrPlaneDetectorSemanticTypeEXT[]
|
||||||
|
public uint maxPlanes;
|
||||||
|
public float minArea;
|
||||||
|
public XrPosef boundingBoxPose;
|
||||||
|
public XrExtent3DfEXT boundingBoxExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorGetInfoEXT">XrPlaneDetectorGetInfoEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public struct XrPlaneDetectorGetInfoEXT
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public XrSpace baseSpace;
|
||||||
|
public XrTime time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorLocationEXT">XrPlaneDetectorLocationEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public struct XrPlaneDetectorLocationEXT
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public ulong planeId;
|
||||||
|
public XrSpaceLocationFlags locationFlags;
|
||||||
|
public XrPosef pose;
|
||||||
|
public XrExtent2Df extents;
|
||||||
|
public XrPlaneDetectorOrientationEXT orientation;
|
||||||
|
public XrPlaneDetectorSemanticTypeEXT semanticType;
|
||||||
|
public uint polygonBufferCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorLocationsEXT">XrPlaneDetectorLocationsEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public struct XrPlaneDetectorLocationsEXT
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public uint planeLocationCapacityInput;
|
||||||
|
public uint planeLocationCountOutput;
|
||||||
|
public IntPtr planeLocations; // XrPlaneDetectorLocationEXT[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorPolygonBufferEXT">XrPlaneDetectorPolygonBufferEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
public struct XrPlaneDetectorPolygonBufferEXT
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public uint vertexCapacityInput;
|
||||||
|
public uint vertexCountOutput;
|
||||||
|
public IntPtr vertices; // XrVector2f[]
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region delegates and delegate instances
|
||||||
|
|
||||||
|
delegate XrResult DelegateXrCreatePlaneDetectorEXT(XrSession session, ref XrPlaneDetectorCreateInfoEXT createInfo, ref IntPtr/*XrPlaneDetectorEXT*/
|
||||||
|
planeDetector);
|
||||||
|
delegate XrResult DelegateXrDestroyPlaneDetectorEXT(IntPtr/*XrPlaneDetectorEXT*/ planeDetector);
|
||||||
|
delegate XrResult DelegateXrBeginPlaneDetectionEXT(IntPtr/*XrPlaneDetectorEXT*/ planeDetector, ref XrPlaneDetectorBeginInfoEXT beginInfo);
|
||||||
|
delegate XrResult DelegateXrGetPlaneDetectionStateEXT(IntPtr/*XrPlaneDetectorEXT*/planeDetector, ref XrPlaneDetectionStateEXT state);
|
||||||
|
delegate XrResult DelegateXrGetPlaneDetectionsEXT(IntPtr/*XrPlaneDetectorEXT*/planeDetector, ref XrPlaneDetectorGetInfoEXT info, ref XrPlaneDetectorLocationsEXT locations);
|
||||||
|
delegate XrResult DelegateXrGetPlanePolygonBufferEXT(IntPtr/*XrPlaneDetectorEXT*/planeDetector, ulong planeId, uint polygonBufferIndex, ref XrPlaneDetectorPolygonBufferEXT polygonBuffer);
|
||||||
|
|
||||||
|
DelegateXrCreatePlaneDetectorEXT XrCreatePlaneDetectorEXT;
|
||||||
|
DelegateXrDestroyPlaneDetectorEXT XrDestroyPlaneDetectorEXT;
|
||||||
|
DelegateXrBeginPlaneDetectionEXT XrBeginPlaneDetectionEXT;
|
||||||
|
DelegateXrGetPlaneDetectionStateEXT XrGetPlaneDetectionStateEXT;
|
||||||
|
DelegateXrGetPlaneDetectionsEXT XrGetPlaneDetectionsEXT;
|
||||||
|
DelegateXrGetPlanePolygonBufferEXT XrGetPlanePolygonBufferEXT;
|
||||||
|
#endregion delegates and delegate instances
|
||||||
|
|
||||||
|
#region override functions
|
||||||
|
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
|
{
|
||||||
|
return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||||
|
{
|
||||||
|
//Debug.Log("VIVEPD OnInstanceCreate() ");
|
||||||
|
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
||||||
|
{
|
||||||
|
Debug.LogWarning("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_XrInstanceCreated = true;
|
||||||
|
m_XrInstance = xrInstance;
|
||||||
|
//Debug.Log("OnInstanceCreate() " + m_XrInstance);
|
||||||
|
|
||||||
|
CommonWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
|
||||||
|
SpaceWrapper.Instance.OnInstanceCreate(xrInstance, CommonWrapper.Instance.GetInstanceProcAddr);
|
||||||
|
|
||||||
|
return GetXrFunctionDelegates(m_XrInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||||
|
{
|
||||||
|
CommonWrapper.Instance.OnInstanceDestroy();
|
||||||
|
SpaceWrapper.Instance.OnInstanceDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnSessionCreate(ulong xrSession)
|
||||||
|
{
|
||||||
|
Debug.Log("VIVEPD OnSessionCreate() ");
|
||||||
|
|
||||||
|
// here's one way you can grab the session
|
||||||
|
Debug.Log($"EXT: Got xrSession: {xrSession}");
|
||||||
|
session = xrSession;
|
||||||
|
m_XrSessionCreated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnSessionBegin(ulong xrSession)
|
||||||
|
{
|
||||||
|
Debug.Log("VIVEPD OnSessionBegin() ");
|
||||||
|
|
||||||
|
Debug.Log($"EXT: xrBeginSession: {xrSession}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnSessionEnd(ulong xrSession)
|
||||||
|
{
|
||||||
|
Debug.Log("VIVEPD OnSessionEnd() ");
|
||||||
|
|
||||||
|
Debug.Log($"EXT: about to xrEndSession: {xrSession}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX Every millisecond the AppSpace switched from one space to another space. I don't know what is going on.
|
||||||
|
//private ulong appSpace;
|
||||||
|
//protected override void OnAppSpaceChange(ulong space)
|
||||||
|
//{
|
||||||
|
// Debug.Log($"VIVEPD OnAppSpaceChange({appSpace} -> {space})");
|
||||||
|
// appSpace = space;
|
||||||
|
//}
|
||||||
|
|
||||||
|
protected override void OnSystemChange(ulong xrSystem)
|
||||||
|
{
|
||||||
|
m_XrSystemId = xrSystem;
|
||||||
|
Debug.Log("OnSystemChange() " + m_XrSystemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion override functions
|
||||||
|
|
||||||
|
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||||
|
{
|
||||||
|
Debug.Log("VIVEPD GetXrFunctionDelegates() ");
|
||||||
|
|
||||||
|
bool ret = true;
|
||||||
|
IntPtr funcPtr = IntPtr.Zero;
|
||||||
|
OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr = CommonWrapper.Instance.GetInstanceProcAddr; // shorter name
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrCreatePlaneDetectorEXT", out XrCreatePlaneDetectorEXT);
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrDestroyPlaneDetectorEXT", out XrDestroyPlaneDetectorEXT);
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrBeginPlaneDetectionEXT", out XrBeginPlaneDetectionEXT);
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrGetPlaneDetectionStateEXT", out XrGetPlaneDetectionStateEXT);
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrGetPlaneDetectionsEXT", out XrGetPlaneDetectionsEXT);
|
||||||
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrGetPlanePolygonBufferEXT", out XrGetPlanePolygonBufferEXT);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region functions of extension
|
||||||
|
/// <summary>
|
||||||
|
/// Helper function to get this feature' properties.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||||
|
/// </summary>
|
||||||
|
public XrResult GetProperties(out XrSystemPlaneDetectionPropertiesEXT properties)
|
||||||
|
{
|
||||||
|
properties = new XrSystemPlaneDetectionPropertiesEXT();
|
||||||
|
properties.type = XrStructureType.XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT;
|
||||||
|
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
{
|
||||||
|
properties.supportedFeatures = CAPABILITY_PLANE_DETECTION_BIT_EXT;
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
Debug.LogError("GetProperties() XR_ERROR_SESSION_LOST.");
|
||||||
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
}
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
Debug.LogError("GetProperties() XR_ERROR_INSTANCE_LOST.");
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommonWrapper.Instance.GetProperties(m_XrInstance, m_XrSystemId, ref properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a PlaneDetector with <paramref name="createInfo"/>. XrSession is implied. The output handle need be destroyed.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreatePlaneDetectorEXT">xrCreatePlaneDetectorEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="createInfo">Fill flags for detection engine.</param>
|
||||||
|
/// <param name="planeDetector">The output detector's handle.</param>
|
||||||
|
/// <seealso cref="DestroyPlaneDetector"/>
|
||||||
|
public XrResult CreatePlaneDetector(XrPlaneDetectorCreateInfoEXT createInfo, out IntPtr/*XrPlaneDetectorEXT*/ planeDetector)
|
||||||
|
{
|
||||||
|
planeDetector = IntPtr.Zero;
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
#endif
|
||||||
|
return XrCreatePlaneDetectorEXT(session, ref createInfo, ref planeDetector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destroy the PlaneDetector handle.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroyPlaneDetectorEXT">xrDestroyPlaneDetectorEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="planeDetector">The detector's handle to be destroyed.</param>
|
||||||
|
/// <seealso cref="CreatePlaneDetector"/>
|
||||||
|
public XrResult DestroyPlaneDetector(IntPtr/*XrPlaneDetectorEXT*/ planeDetector)
|
||||||
|
{
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
#endif
|
||||||
|
return XrDestroyPlaneDetectorEXT(planeDetector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Let Detector start to work.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrBeginPlaneDetectionEXT">xrBeginPlaneDetectionEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="planeDetector">The detector's handle to be begined.</param>
|
||||||
|
/// <param name="beginInfo">Fill flags for detection engine. You can use the result of MakeGetAllXrPlaneDetectorBeginInfoEXT.</param>
|
||||||
|
/// <seealso cref="MakeGetAllXrPlaneDetectorBeginInfoEXT"/>
|
||||||
|
public XrResult BeginPlaneDetection(IntPtr/*XrPlaneDetectorEXT*/ planeDetector, XrPlaneDetectorBeginInfoEXT beginInfo)
|
||||||
|
{
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
#endif
|
||||||
|
return XrBeginPlaneDetectionEXT(planeDetector, ref beginInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check PlaneDetector's state. If the state is DONE_EXT, you can get the result by GetPlaneDetections.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetPlaneDetectionStateEXT">xrGetPlaneDetectionStateEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="planeDetector">The detector's state to be check.</param>
|
||||||
|
/// <param name="state">Fill flags for detection engine. You can use the result of MakeGetAllXrPlaneDetectorBeginInfoEXT.</param>
|
||||||
|
public XrResult GetPlaneDetectionState(IntPtr/*XrPlaneDetectorEXT*/ planeDetector, ref XrPlaneDetectionStateEXT state)
|
||||||
|
{
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
{
|
||||||
|
state = XrPlaneDetectionStateEXT.DONE_EXT;
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return XrGetPlaneDetectionStateEXT(planeDetector, ref state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the result of PlaneDetector.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetPlaneDetectionsEXT">xrGetPlaneDetectionsEXT</see>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="planeDetector">The detector's state to be check.</param>
|
||||||
|
/// <param name="info">Use info to specify the data's space.</param>
|
||||||
|
/// <param name="locations">The output data.</param>
|
||||||
|
public XrResult GetPlaneDetections(IntPtr/*XrPlaneDetectorEXT*/ planeDetector, ref XrPlaneDetectorGetInfoEXT info, ref XrPlaneDetectorLocationsEXT locations)
|
||||||
|
{
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
{
|
||||||
|
locations.planeLocationCountOutput = 1;
|
||||||
|
if (locations.planeLocationCapacityInput == 0)
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
if (locations.planeLocationCapacityInput < 1 || locations.planeLocations == IntPtr.Zero)
|
||||||
|
return XrResult.XR_ERROR_SIZE_INSUFFICIENT;
|
||||||
|
|
||||||
|
locations.planeLocationCountOutput = 1;
|
||||||
|
XrPlaneDetectorLocationEXT location = new XrPlaneDetectorLocationEXT();
|
||||||
|
location.planeId = 1;
|
||||||
|
location.extents = new XrExtent2Df(1, 1);
|
||||||
|
location.locationFlags = XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XrSpaceLocationFlags.XR_SPACE_LOCATION_POSITION_VALID_BIT;
|
||||||
|
location.semanticType = XrPlaneDetectorSemanticTypeEXT.FLOOR_EXT;
|
||||||
|
location.polygonBufferCount = 1;
|
||||||
|
location.pose = new XrPosef(XrQuaternionf.Identity, new XrVector3f(0, 1, -1)); // This plane will face the Z axis
|
||||||
|
location.orientation = XrPlaneDetectorOrientationEXT.VERTICAL_EXT;
|
||||||
|
|
||||||
|
Marshal.StructureToPtr(location, locations.planeLocations, false);
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return XrGetPlaneDetectionsEXT(planeDetector, ref info, ref locations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the vertex buffer of a plane.
|
||||||
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetPlanePolygonBufferEXT">GetPlanePolygonBuffer</see>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="planeDetector">The detector's state to be check.</param>
|
||||||
|
/// <param name="planeId">The target plane's planeId. Get it from <see cref="XrPlaneDetectorLocationEXT" />.</param>
|
||||||
|
/// <param name="polygonBufferIndex">The buffer index in the plane. Get it from <see cref="XrPlaneDetectorLocationEXT" />. Currently VIVE will only return 1 buffer for each plane.</param>
|
||||||
|
/// <param name="polygonBuffer">The output data.</param>
|
||||||
|
public XrResult GetPlanePolygonBuffer(IntPtr/*XrPlaneDetectorEXT*/ planeDetector, ulong planeId, uint polygonBufferIndex, ref XrPlaneDetectorPolygonBufferEXT polygonBuffer)
|
||||||
|
{
|
||||||
|
#if FAKE_DATA
|
||||||
|
if (Application.isEditor)
|
||||||
|
{
|
||||||
|
if (planeId != 1) return XrResult.XR_ERROR_NAME_INVALID;
|
||||||
|
if (polygonBufferIndex != 0) return XrResult.XR_ERROR_INDEX_OUT_OF_RANGE;
|
||||||
|
polygonBuffer.vertexCountOutput = 4;
|
||||||
|
if (polygonBuffer.vertexCapacityInput == 0)
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
if (polygonBuffer.vertexCapacityInput != 4 || polygonBuffer.vertices == IntPtr.Zero)
|
||||||
|
return XrResult.XR_ERROR_SIZE_INSUFFICIENT;
|
||||||
|
XrVector2f[] vertices = new XrVector2f[4];
|
||||||
|
// Make a plane's contour
|
||||||
|
vertices[0] = new XrVector2f(-0.5f, -0.5f);
|
||||||
|
vertices[1] = new XrVector2f( 0.5f, -0.5f);
|
||||||
|
vertices[2] = new XrVector2f( 0.5f, 0.5f);
|
||||||
|
vertices[3] = new XrVector2f(-0.5f, 0.5f);
|
||||||
|
|
||||||
|
MemoryTools.CopyToRawMemory(polygonBuffer.vertices, vertices);
|
||||||
|
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return XrGetPlanePolygonBufferEXT(planeDetector, planeId, polygonBufferIndex, ref polygonBuffer);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region tools for user
|
||||||
|
/// <summary>
|
||||||
|
/// A helper function to generate XrPlaneDetectorBeginInfoEXT. VIVE didn't implement all possible features of this extension.
|
||||||
|
/// Hardcode some parameters here.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="BeginPlaneDetection"/>
|
||||||
|
public XrPlaneDetectorBeginInfoEXT MakeGetAllXrPlaneDetectorBeginInfoEXT()
|
||||||
|
{
|
||||||
|
XrPlaneDetectorBeginInfoEXT beginInfo = new XrPlaneDetectorBeginInfoEXT
|
||||||
|
{
|
||||||
|
type = XrStructureType.XR_TYPE_PLANE_DETECTOR_BEGIN_INFO_EXT,
|
||||||
|
baseSpace = new XrSpace(GetCurrentAppSpace()), // Cannot depend on GetCurrentAppSpace...
|
||||||
|
//baseSpace = GetTrackingSpace(),
|
||||||
|
time = ViveInterceptors.Instance.GetPredictTime(),
|
||||||
|
orientationCount = 0, // Any orientation
|
||||||
|
orientations = IntPtr.Zero, // XrPlaneDetectorOrientationEXT[]
|
||||||
|
semanticTypeCount = 0, // Any semantic type
|
||||||
|
semanticTypes = IntPtr.Zero, // XrPlaneDetectorSemanticTypeEXT[]
|
||||||
|
maxPlanes = 10000, // Hopefully enough
|
||||||
|
minArea = 0.1f, // 10cm^2
|
||||||
|
boundingBoxPose = XrPosef.Identity,
|
||||||
|
boundingBoxExtent = new XrExtent3DfEXT(1000, 1000, 1000),
|
||||||
|
};
|
||||||
|
return beginInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time here is only used for info of GetPlaneDetections. Not the real predictTime of XrWaitFrame.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrTime GetPredictTime()
|
||||||
|
{
|
||||||
|
return ViveInterceptors.Instance.GetPredictTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// According to XRInputSubsystem's tracking origin mode, return the corresponding XrSpace.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrSpace GetTrackingSpace()
|
||||||
|
{
|
||||||
|
return GetCurrentAppSpace();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cbc636a69caaad0418c5e52e22103f2e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -312,6 +312,36 @@ namespace VIVE.OpenXR
|
|||||||
XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR,
|
XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR,
|
||||||
XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR,
|
XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR,
|
||||||
XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR,
|
XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR,
|
||||||
|
XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT = 1000429001,
|
||||||
|
XR_TYPE_PLANE_DETECTOR_BEGIN_INFO_EXT = 1000429002,
|
||||||
|
XR_TYPE_PLANE_DETECTOR_GET_INFO_EXT = 1000429003,
|
||||||
|
XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT = 1000429004,
|
||||||
|
XR_TYPE_PLANE_DETECTOR_LOCATION_EXT = 1000429005,
|
||||||
|
XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT = 1000429006,
|
||||||
|
XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT = 1000429007,
|
||||||
|
XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC = 1000317001,
|
||||||
|
XR_TYPE_PASSTHROUGH_COLOR_HTC = 1000317002,
|
||||||
|
XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC = 1000317003,
|
||||||
|
XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC = 1000317004,
|
||||||
|
XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC = 1000319000,
|
||||||
|
XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC = 1000319001,
|
||||||
|
XR_TYPE_PERSISTED_ANCHOR_COLLECTION_CREATE_INFO_HTC = 1000320000,
|
||||||
|
XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_HTC = 1000320001,
|
||||||
|
XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_HTC = 1000320002,
|
||||||
|
XR_TYPE_PERSISTED_ANCHOR_PROPERTIES_GET_INFO_HTC = 1000320003,
|
||||||
|
XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB = 1000171000,
|
||||||
|
XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB = 1000171001,
|
||||||
|
XR_TYPE_EYE_TRACKER_CREATE_INFO_HTC = 1000326001,
|
||||||
|
XR_TYPE_EYE_GAZE_DATA_INFO_HTC = 1000326002,
|
||||||
|
XR_TYPE_EYE_GAZE_DATA_HTC = 1000326003,
|
||||||
|
XR_TYPE_SINGLE_EYE_GAZE_DATA_HTC = 1000326004,
|
||||||
|
XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_HTC = 1000326005,
|
||||||
|
XR_TYPE_EYE_PUPIL_DATA_INFO_HTC = 1000326006,
|
||||||
|
XR_TYPE_EYE_PUPIL_DATA_HTC = 1000326007,
|
||||||
|
XR_TYPE_SINGLE_EYE_PUPIL_DATA_HTC = 1000326008,
|
||||||
|
XR_TYPE_EYE_GEOMETRIC_DATA_INFO_HTC = 1000326009,
|
||||||
|
XR_TYPE_EYE_GEOMETRIC_DATA_HTC = 1000326010,
|
||||||
|
XR_TYPE_SINGLE_EYE_GEOMETRIC_DATA_HTC = 1000326011,
|
||||||
XR_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
XR_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -474,7 +504,13 @@ namespace VIVE.OpenXR
|
|||||||
y = in_y;
|
y = in_y;
|
||||||
z = in_z;
|
z = in_z;
|
||||||
}
|
}
|
||||||
}
|
public static XrVector3f Zero => new XrVector3f(0, 0, 0);
|
||||||
|
public static XrVector3f One => new XrVector3f(1, 1, 1);
|
||||||
|
public static XrVector3f Up => new XrVector3f(0, 1, 0);
|
||||||
|
public static XrVector3f Forward => new XrVector3f(0, 0, 1);
|
||||||
|
public static XrVector3f Right => new XrVector3f(1, 0, 0);
|
||||||
|
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rotation is represented by a unit quaternion defined by the XrQuaternionf structure.
|
/// Rotation is represented by a unit quaternion defined by the XrQuaternionf structure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -507,6 +543,7 @@ namespace VIVE.OpenXR
|
|||||||
z = in_z;
|
z = in_z;
|
||||||
w = in_w;
|
w = in_w;
|
||||||
}
|
}
|
||||||
|
public static XrQuaternionf Identity => new XrQuaternionf(0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unless otherwise specified, colors are encoded as linear (not with sRGB nor other gamma compression) values with individual components being in the range of 0.0 through 1.0, and without the RGB components being premultiplied by the alpha component.
|
/// Unless otherwise specified, colors are encoded as linear (not with sRGB nor other gamma compression) values with individual components being in the range of 0.0 through 1.0, and without the RGB components being premultiplied by the alpha component.
|
||||||
@@ -640,6 +677,12 @@ namespace VIVE.OpenXR
|
|||||||
/// An <see cref="XrVector3f">XrVector3f</see> representing position within a space.
|
/// An <see cref="XrVector3f">XrVector3f</see> representing position within a space.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public XrVector3f position;
|
public XrVector3f position;
|
||||||
|
public XrPosef(XrQuaternionf in_orientation, XrVector3f in_position)
|
||||||
|
{
|
||||||
|
orientation = in_orientation;
|
||||||
|
position = in_position;
|
||||||
|
}
|
||||||
|
public static XrPosef Identity => new XrPosef(XrQuaternionf.Identity, XrVector3f.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1221,6 +1264,15 @@ namespace VIVE.OpenXR
|
|||||||
XR_SPACE_LOCATION_POSITION_TRACKED_BIT = 0x00000008,
|
XR_SPACE_LOCATION_POSITION_TRACKED_BIT = 0x00000008,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct XrSpaceLocation
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public System.IntPtr next;
|
||||||
|
public XrSpaceLocationFlags locationFlags;
|
||||||
|
public XrPosef pose;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
public enum XrInputSourceLocalizedNameFlags : UInt64
|
public enum XrInputSourceLocalizedNameFlags : UInt64
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1931,7 +1983,93 @@ namespace VIVE.OpenXR
|
|||||||
public IntPtr next;
|
public IntPtr next;
|
||||||
public XrBool32 isActive;
|
public XrBool32 isActive;
|
||||||
};
|
};
|
||||||
|
/// <summary>
|
||||||
|
/// A structure indicates the frameWaitInfo.
|
||||||
|
/// </summary>
|
||||||
|
public struct XrFrameWaitInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// next is NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
public XrFrameWaitInfo(IntPtr next_, XrStructureType type_)
|
||||||
|
{
|
||||||
|
next = next_;
|
||||||
|
type = type_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// A structure indicates the frameState.
|
||||||
|
/// </summary>
|
||||||
|
public struct XrFrameState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// next is NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
/// <summary>
|
||||||
|
/// predictedDisplayTime is the anticipated display XrTime for the next application-generated frame.
|
||||||
|
/// </summary>
|
||||||
|
public Int64 predictedDisplayTime;
|
||||||
|
/// <summary>
|
||||||
|
/// predictedDisplayPeriod is the XrDuration of the display period for the next application-generated frame, for use in predicting display times beyond the next one.
|
||||||
|
/// </summary>
|
||||||
|
public Int64 predictedDisplayPeriod;
|
||||||
|
/// <summary>
|
||||||
|
/// shouldRender is XR_TRUE if the application should render its layers as normal and submit them to xrEndFrame. When this value is XR_FALSE, the application should avoid heavy GPU work where possible, for example by skipping layer rendering and then omitting those layers when calling xrEndFrame.
|
||||||
|
/// </summary>
|
||||||
|
public bool shouldRender;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A structure indicates the XrFrameEndInfo .
|
||||||
|
/// </summary>
|
||||||
|
public struct XrFrameEndInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// next is NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
/// <summary>
|
||||||
|
/// displayTime is the XrTime at which this frame should be displayed.
|
||||||
|
/// </summary>
|
||||||
|
public XrTime displayTime;
|
||||||
|
/// <summary>
|
||||||
|
/// environmentBlendMode is the XrEnvironmentBlendMode value representing the desired environment blend mode for this frame.
|
||||||
|
/// </summary>
|
||||||
|
public XrEnvironmentBlendMode environmentBlendMode;
|
||||||
|
/// <summary>
|
||||||
|
/// layerCount is the number of composition layers in this frame. The maximum supported layer count is identified by XrSystemGraphicsProperties::maxLayerCount.
|
||||||
|
/// </summary>
|
||||||
|
public uint layerCount;
|
||||||
|
/// <summary>
|
||||||
|
/// layers is a pointer to an array of XrCompositionLayerBaseHeader pointers.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr layers;
|
||||||
|
|
||||||
|
public XrFrameEndInfo(XrStructureType type_,IntPtr next_, XrTime displayTime_, XrEnvironmentBlendMode environmentBlendMode_
|
||||||
|
,uint layerCount_, IntPtr layers_)
|
||||||
|
{
|
||||||
|
next = next_;
|
||||||
|
type = type_;
|
||||||
|
displayTime = displayTime_;
|
||||||
|
environmentBlendMode = environmentBlendMode_;
|
||||||
|
layerCount = layerCount_;
|
||||||
|
layers = layers_;
|
||||||
|
}
|
||||||
|
}
|
||||||
public static class OpenXRHelper
|
public static class OpenXRHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -2224,6 +2362,59 @@ namespace VIVE.OpenXR
|
|||||||
ref UInt32 bufferCountOutput,
|
ref UInt32 bufferCountOutput,
|
||||||
[In, Out] char[] buffer);
|
[In, Out] char[] buffer);
|
||||||
|
|
||||||
|
public static XrResult GetInputSourceName(
|
||||||
|
xrGetInputSourceLocalizedNameDelegate xrGetInputSourceLocalizedName,
|
||||||
|
XrSession session,
|
||||||
|
ref XrInputSourceLocalizedNameGetInfo nameInfo,
|
||||||
|
out string sourceName)
|
||||||
|
{
|
||||||
|
string func = "GetInputSourceName() ";
|
||||||
|
|
||||||
|
sourceName = "";
|
||||||
|
if (xrGetInputSourceLocalizedName == null) { return XrResult.XR_ERROR_VALIDATION_FAILURE; }
|
||||||
|
|
||||||
|
sb.Clear().Append(LOG_TAG).Append(func).Append("path: ").Append(nameInfo.sourcePath).Append(", flag: ").Append((UInt64)nameInfo.whichComponents); DEBUG(sb);
|
||||||
|
|
||||||
|
UInt32 nameSizeIn = 0;
|
||||||
|
UInt32 nameSizeOut = 0;
|
||||||
|
char[] buffer = new char[0];
|
||||||
|
|
||||||
|
XrResult result = xrGetInputSourceLocalizedName(session, ref nameInfo, nameSizeIn, ref nameSizeOut, buffer);
|
||||||
|
sb.Clear().Append(LOG_TAG).Append(func)
|
||||||
|
.Append("1.xrGetInputSourceLocalizedName(").Append(nameInfo.sourcePath).Append(") result: ").Append(result)
|
||||||
|
.Append(", flag: ").Append((UInt64)nameInfo.whichComponents)
|
||||||
|
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
|
||||||
|
.Append(", bufferCountOutput: ").Append(nameSizeOut);
|
||||||
|
DEBUG(sb);
|
||||||
|
if (result == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (nameSizeOut < 1)
|
||||||
|
{
|
||||||
|
sb.Clear().Append(LOG_TAG).Append(func)
|
||||||
|
.Append("xrGetInputSourceLocalizedName(").Append(nameInfo.sourcePath).Append(")")
|
||||||
|
.Append(", flag: ").Append((UInt64)nameInfo.whichComponents)
|
||||||
|
.Append("bufferCountOutput size is invalid!");
|
||||||
|
ERROR(sb);
|
||||||
|
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nameSizeIn = nameSizeOut;
|
||||||
|
buffer = new char[nameSizeIn];
|
||||||
|
|
||||||
|
result = xrGetInputSourceLocalizedName(session, ref nameInfo, nameSizeIn, ref nameSizeOut, buffer);
|
||||||
|
sb.Clear().Append(LOG_TAG).Append(func)
|
||||||
|
.Append("2.xrGetInputSourceLocalizedName(").Append(nameInfo.sourcePath).Append(") result: ").Append(result)
|
||||||
|
.Append(", flag: ").Append((UInt64)nameInfo.whichComponents)
|
||||||
|
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
|
||||||
|
.Append(", bufferCountOutput: ").Append(nameSizeOut);
|
||||||
|
DEBUG(sb);
|
||||||
|
if (result == XrResult.XR_SUCCESS) { sourceName = new string(buffer).TrimEnd('\0'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public delegate XrResult xrEnumerateInstanceExtensionPropertiesDelegate(
|
public delegate XrResult xrEnumerateInstanceExtensionPropertiesDelegate(
|
||||||
[In] char[] layerName,
|
[In] char[] layerName,
|
||||||
UInt32 propertyCapacityInput,
|
UInt32 propertyCapacityInput,
|
||||||
@@ -2313,9 +2504,46 @@ namespace VIVE.OpenXR
|
|||||||
XrSession session,
|
XrSession session,
|
||||||
ref XrActionStateGetInfo getInfo,
|
ref XrActionStateGetInfo getInfo,
|
||||||
ref XrActionStatePose state);
|
ref XrActionStatePose state);
|
||||||
|
/// <summary>
|
||||||
|
/// The function delegate declaration of <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrWaitFrame">xrWaitFrame</see>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="session">A handle to an XrSession previously created with <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateSession">xrCreateSession</see>.</param>
|
||||||
|
/// <param name="frameWaitInfo">frameWaitInfo exists for extensibility purposes, it is NULL or a pointer to a valid XrFrameWaitInfo.</param>
|
||||||
|
/// <param name="frameState">frameState is a pointer to a valid XrFrameState, an output parameter.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public delegate int xrWaitFrameDelegate(ulong session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState);
|
||||||
|
public delegate XrResult xrEndFrameDelegate(XrSession session, ref XrFrameEndInfo frameEndInfo);
|
||||||
|
/// <summary>
|
||||||
|
/// Help call xrGetInstanceProcAddr and convert the result to delegate.\
|
||||||
|
/// For example, "OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrGetSystemProperties", out XrGetSystemProperties);"
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="Type">The function's delegate.</typeparam>
|
||||||
|
/// <param name="XrGetInstanceProcAddr">Your xrGetInstanceProcAddr delegate instance.</param>
|
||||||
|
/// <param name="xrInstance">Your xrInstance</param>
|
||||||
|
/// <param name="name">The function name</param>
|
||||||
|
/// <param name="func">The output delegate instance.</param>
|
||||||
|
/// <returns>If return false, the outout delegate instance will be default. Should not use it.</returns>
|
||||||
|
public static bool GetXrFunctionDelegate<Type>(xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr, XrInstance xrInstance, string name, out Type func)
|
||||||
|
{
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, name, out var funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Debug.Log("Get function pointer of " + name);
|
||||||
|
func = Marshal.GetDelegateForFunctionPointer<Type>(funcPtr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError("GetXrFunctionDelegate: fail to get address of function: " + name);
|
||||||
|
}
|
||||||
|
func = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ClientInterface
|
public static class ClientInterface
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the user is presence (near HMD p-sensor < 1cm).
|
/// Checks if the user is presence (near HMD p-sensor < 1cm).
|
||||||
|
|||||||
143
com.htc.upm.vive.openxr/Runtime/Profiles/XR_HTC_eye_tracker.cs
Normal file
143
com.htc.upm.vive.openxr/Runtime/Profiles/XR_HTC_eye_tracker.cs
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// ===================== 2022 HTC Corporation. All Rights Reserved. ===================
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using VIVE.OpenXR.EyeTracker;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
public class XR_HTC_eye_tracker_defs
|
||||||
|
{
|
||||||
|
public virtual XrResult xrCreateEyeTrackerHTC(XrEyeTrackerCreateInfoHTC createInfo, out XrEyeTrackerHTC eyeTracker)
|
||||||
|
{
|
||||||
|
eyeTracker = 0;
|
||||||
|
return XrResult.XR_ERROR_FEATURE_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
public virtual XrResult xrDestroyEyeTrackerHTC(XrEyeTrackerHTC eyeTracker)
|
||||||
|
{
|
||||||
|
return XrResult.XR_ERROR_FEATURE_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
protected XrEyeGazeDataHTC m_eyeGazes = new XrEyeGazeDataHTC(); //= new XrEyeGazeDataHTC(XrStructureType.XR_TYPE_EYE_GAZE_DATA_HTC, IntPtr.Zero, 0);
|
||||||
|
public virtual XrResult xrGetEyeGazeDataHTC(XrEyeTrackerHTC eyeTracker, XrEyeGazeDataInfoHTC gazeInfo, out XrEyeGazeDataHTC eyeGazes)
|
||||||
|
{
|
||||||
|
m_eyeGazes.type = XrStructureType.XR_TYPE_EYE_GAZE_DATA_HTC;
|
||||||
|
m_eyeGazes.next = IntPtr.Zero;
|
||||||
|
m_eyeGazes.time = 0;
|
||||||
|
eyeGazes = m_eyeGazes;
|
||||||
|
return XrResult.XR_ERROR_FEATURE_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
public virtual bool GetEyeGazeData(out XrSingleEyeGazeDataHTC[] out_gazes)
|
||||||
|
{
|
||||||
|
m_eyeGazes.type = XrStructureType.XR_TYPE_EYE_GAZE_DATA_HTC;
|
||||||
|
m_eyeGazes.next = IntPtr.Zero;
|
||||||
|
m_eyeGazes.time = 0;
|
||||||
|
out_gazes = m_eyeGazes.gaze;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
protected XrEyePupilDataHTC m_pupilData = new XrEyePupilDataHTC();
|
||||||
|
public virtual XrResult xrGetEyePupilDataHTC(XrEyeTrackerHTC eyeTracker, XrEyePupilDataInfoHTC pupilDataInfo, out XrEyePupilDataHTC pupilData)
|
||||||
|
{
|
||||||
|
m_pupilData.type = XrStructureType.XR_TYPE_EYE_PUPIL_DATA_HTC;
|
||||||
|
m_pupilData.next = IntPtr.Zero;
|
||||||
|
m_pupilData.time = 0;
|
||||||
|
pupilData = m_pupilData;
|
||||||
|
return XrResult.XR_ERROR_FEATURE_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
public virtual bool GetEyePupilData(out XrSingleEyePupilDataHTC[] pupilData)
|
||||||
|
{
|
||||||
|
m_pupilData.type = XrStructureType.XR_TYPE_EYE_PUPIL_DATA_HTC;
|
||||||
|
m_pupilData.next = IntPtr.Zero;
|
||||||
|
m_pupilData.time = 0;
|
||||||
|
pupilData = m_pupilData.pupilData;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
protected XrEyeGeometricDataHTC m_eyeGeometricData = new XrEyeGeometricDataHTC();
|
||||||
|
public virtual XrResult xrGetEyeGeometricDataHTC(XrEyeTrackerHTC eyeTracker,
|
||||||
|
XrEyeGeometricDataInfoHTC info,
|
||||||
|
out XrEyeGeometricDataHTC eyeGeometricData)
|
||||||
|
{
|
||||||
|
m_eyeGeometricData.type = XrStructureType.XR_TYPE_EYE_GEOMETRIC_DATA_HTC;
|
||||||
|
m_eyeGeometricData.next = IntPtr.Zero;
|
||||||
|
m_eyeGeometricData.time = 0;
|
||||||
|
eyeGeometricData = m_eyeGeometricData;
|
||||||
|
return XrResult.XR_ERROR_FEATURE_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
public virtual bool GetEyeGeometricData(out XrSingleEyeGeometricDataHTC[] geometricData)
|
||||||
|
{
|
||||||
|
m_eyeGeometricData.type = XrStructureType.XR_TYPE_EYE_GEOMETRIC_DATA_HTC;
|
||||||
|
m_eyeGeometricData.next = IntPtr.Zero;
|
||||||
|
m_eyeGeometricData.time = 0;
|
||||||
|
geometricData = m_eyeGeometricData.eyeGeometricData;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class XR_HTC_eye_tracker
|
||||||
|
{
|
||||||
|
static XR_HTC_eye_tracker_defs m_Instance = null;
|
||||||
|
public static XR_HTC_eye_tracker_defs Interop
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_Instance == null)
|
||||||
|
{
|
||||||
|
m_Instance = new XR_HTC_eye_tracker_impls();
|
||||||
|
}
|
||||||
|
return m_Instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An application can create an <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> handle using CreateEyeTracker.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="createInfo">The <see cref="XrEyeTrackerCreateInfoHTC">XrEyeTrackerCreateInfoHTC</see> used to specify the eye tracker.</param>
|
||||||
|
/// <param name="eyeTracker">The returned XrEyeTrackerHTC handle.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public static XrResult xrCreateEyeTrackerHTC(XrEyeTrackerCreateInfoHTC createInfo, out XrEyeTrackerHTC eyeTracker)
|
||||||
|
{
|
||||||
|
return Interop.xrCreateEyeTrackerHTC(createInfo,out eyeTracker);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Releases the eye tracker and the underlying resources when the eye tracking experience is over.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eyeTracker">An XrEyeTrackerHTC previously created by xrCreateEyeTrackerHTC.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public static XrResult xrDestroyEyeTrackerHTC(XrEyeTrackerHTC eyeTracker)
|
||||||
|
{
|
||||||
|
return Interop.xrDestroyEyeTrackerHTC(eyeTracker);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="XrEyeGazeDataHTC">XrEyeGazeDataHTC</see> data of a <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="gazeInfo">The information to get eye gaze.</param>
|
||||||
|
/// <param name="eyeGazes">Output parameter to retrieve a pointer to <see cref="XrEyeGazeDataHTC">XrEyeGazeDataHTC</see> receiving the returned eye poses.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public static XrResult xrGetEyeGazeDataHTC(XrEyeTrackerHTC eyeTracker, XrEyeGazeDataInfoHTC gazeInfo, out XrEyeGazeDataHTC eyeGazes)
|
||||||
|
{
|
||||||
|
return Interop.xrGetEyeGazeDataHTC(eyeTracker, gazeInfo, out eyeGazes);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="XrEyePupilDataHTC">XrEyePupilDataHTC</see> data of a <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="pupilDataInfo">The information to get pupil data.</param>
|
||||||
|
/// <param name="pupilData">A pointer to <see cref="XrEyePupilDataHTC">XrEyePupilDataHTC</see> returned by the runtime.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public static XrResult xrGetEyePupilDataHTC(XrEyeTrackerHTC eyeTracker, XrEyePupilDataInfoHTC pupilDataInfo, out XrEyePupilDataHTC pupilData)
|
||||||
|
{
|
||||||
|
return Interop.xrGetEyePupilDataHTC(eyeTracker, pupilDataInfo, out pupilData);
|
||||||
|
}
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="info">A pointer to <see cref="XrEyeGeometricDataInfoHTC">XrEyeGeometricDataInfoHTC</see> structure.</param>
|
||||||
|
/// <param name="eyeGeometricData">A pointer to <see cref="XrEyeGeometricDataHTC">XrEyeGeometricDataHTC</see> returned by the runtime.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public static XrResult xrGetEyeGeometricDataHTC(XrEyeTrackerHTC eyeTracker,
|
||||||
|
XrEyeGeometricDataInfoHTC info,
|
||||||
|
out XrEyeGeometricDataHTC eyeGeometricData)
|
||||||
|
{
|
||||||
|
return Interop.xrGetEyeGeometricDataHTC(eyeTracker,info, out eyeGeometricData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1fb607ec9f5f9a349841a128ef8b2725
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
// ===================== 2022 HTC Corporation. All Rights Reserved. ===================
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
using UnityEngine.XR.OpenXR;
|
||||||
|
|
||||||
|
using VIVE.OpenXR.EyeTracker;
|
||||||
|
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
public class XR_HTC_eye_tracker_impls : XR_HTC_eye_tracker_defs
|
||||||
|
{
|
||||||
|
const string LOG_TAG = "VIVE.OpenXR.XR_HTC_eye_tracker_impls";
|
||||||
|
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||||
|
|
||||||
|
private ViveEyeTracker feature = null;
|
||||||
|
private void ASSERT_FEATURE()
|
||||||
|
{
|
||||||
|
if (feature == null) { feature = OpenXRSettings.Instance.GetFeature<ViveEyeTracker>(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override XrResult xrCreateEyeTrackerHTC(XrEyeTrackerCreateInfoHTC createInfo, out XrEyeTrackerHTC eyeTracker)
|
||||||
|
{
|
||||||
|
DEBUG("xrCreateEyeTrackerHTC");
|
||||||
|
XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
eyeTracker = 0;
|
||||||
|
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
result = (XrResult)feature.CreateEyeTracker(createInfo, out XrEyeTrackerHTC value);
|
||||||
|
if (result == XrResult.XR_SUCCESS) { eyeTracker = value; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public override XrResult xrDestroyEyeTrackerHTC(XrEyeTrackerHTC eyeTracker)
|
||||||
|
{
|
||||||
|
DEBUG("xrDestroyEyeTrackerHTC");
|
||||||
|
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature) { return (XrResult)feature.DestroyEyeTracker(eyeTracker); }
|
||||||
|
|
||||||
|
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
}
|
||||||
|
public override XrResult xrGetEyeGazeDataHTC(XrEyeTrackerHTC eyeTracker, XrEyeGazeDataInfoHTC gazeInfo, out XrEyeGazeDataHTC eyeGazes)
|
||||||
|
{
|
||||||
|
eyeGazes = m_eyeGazes;
|
||||||
|
XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
result = (XrResult)feature.GetEyeGazeData(eyeTracker, gazeInfo, out XrEyeGazeDataHTC gazes);
|
||||||
|
if (result == XrResult.XR_SUCCESS) { eyeGazes = gazes; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetEyeGazeData(out XrSingleEyeGazeDataHTC[] out_gazes)
|
||||||
|
{
|
||||||
|
out_gazes = m_eyeGazes.gaze;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
result = feature.GetEyeGazeData(out XrSingleEyeGazeDataHTC[] data);
|
||||||
|
if (result) out_gazes = data;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override XrResult xrGetEyePupilDataHTC(XrEyeTrackerHTC eyeTracker, XrEyePupilDataInfoHTC pupilDataInfo, out XrEyePupilDataHTC pupilData)
|
||||||
|
{
|
||||||
|
pupilData = m_pupilData;
|
||||||
|
XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
result = (XrResult)feature.GetEyePupilData(eyeTracker, pupilDataInfo, out XrEyePupilDataHTC data);
|
||||||
|
if (result == XrResult.XR_SUCCESS) { pupilData = data; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetEyePupilData(out XrSingleEyePupilDataHTC[] pupilData)
|
||||||
|
{
|
||||||
|
pupilData = m_pupilData.pupilData;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
result = feature.GetEyePupilData(out XrSingleEyePupilDataHTC[] data);
|
||||||
|
if (result) pupilData = data;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public override XrResult xrGetEyeGeometricDataHTC(XrEyeTrackerHTC eyeTracker,
|
||||||
|
XrEyeGeometricDataInfoHTC info,
|
||||||
|
out XrEyeGeometricDataHTC eyeGeometricData)
|
||||||
|
{
|
||||||
|
eyeGeometricData = m_eyeGeometricData;
|
||||||
|
XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
result = (XrResult)feature.GetEyeGeometricData(eyeTracker, info, out XrEyeGeometricDataHTC geometricData);
|
||||||
|
if (result == XrResult.XR_SUCCESS) { eyeGeometricData = geometricData; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override bool GetEyeGeometricData(out XrSingleEyeGeometricDataHTC[] geometricData)
|
||||||
|
{
|
||||||
|
geometricData = m_eyeGeometricData.eyeGeometricData;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
result = feature.GetEyeGeometricData(out XrSingleEyeGeometricDataHTC[] data);
|
||||||
|
if (result) geometricData = data;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 166bae55cad9f9146a8c2ada64d74832
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using VIVE.OpenXR.CompositionLayer;
|
||||||
|
using VIVE.OpenXR.CompositionLayer.Passthrough;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
public class XR_HTC_passthrough_defs
|
||||||
|
{
|
||||||
|
public virtual XrResult xrCreatePassthroughHTC(XrPassthroughCreateInfoHTC createInfo, out XrPassthroughHTC passthrough)
|
||||||
|
{
|
||||||
|
passthrough = 0;
|
||||||
|
return XrResult.XR_ERROR_RUNTIME_FAILURE;
|
||||||
|
}
|
||||||
|
public virtual XrResult xrDestroyPassthroughHTC(XrPassthroughHTC passthrough)
|
||||||
|
{
|
||||||
|
return XrResult.XR_ERROR_RUNTIME_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void GetOriginEndFrameLayerList(out List<IntPtr> layers)
|
||||||
|
{
|
||||||
|
layers = new List<IntPtr>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void SubmitLayers(List<IntPtr> layers)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public virtual XrSpace GetTrackingSpace()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual XrFrameState GetFrameState()
|
||||||
|
{
|
||||||
|
return new XrFrameState();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public static class XR_HTC_passthrough
|
||||||
|
{
|
||||||
|
static XR_HTC_passthrough_defs m_Instance = null;
|
||||||
|
public static XR_HTC_passthrough_defs Interop
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_Instance == null)
|
||||||
|
{
|
||||||
|
m_Instance = new XR_HTC_passthrough_impls();
|
||||||
|
}
|
||||||
|
return m_Instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static XrResult xrCreatePassthroughHTC(XrPassthroughCreateInfoHTC createInfo, out XrPassthroughHTC passthrough)
|
||||||
|
{
|
||||||
|
return Interop.xrCreatePassthroughHTC(createInfo,out passthrough);
|
||||||
|
}
|
||||||
|
public static XrResult xrDestroyPassthroughHTC(XrPassthroughHTC passthrough)
|
||||||
|
{
|
||||||
|
return Interop.xrDestroyPassthroughHTC(passthrough);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 099596ee936f4724f866de5335ef9cf6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.XR.OpenXR;
|
||||||
|
using VIVE.OpenXR.CompositionLayer;
|
||||||
|
using VIVE.OpenXR.CompositionLayer.Passthrough;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
public class XR_HTC_passthrough_impls : XR_HTC_passthrough_defs
|
||||||
|
{
|
||||||
|
const string LOG_TAG = "VIVE.OpenXR.XR_HTC_passthrough_impls";
|
||||||
|
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||||
|
public XR_HTC_passthrough_impls() { DEBUG("XR_HTC_passthrough_impls()"); }
|
||||||
|
private ViveCompositionLayerPassthrough feature = null;
|
||||||
|
|
||||||
|
private void ASSERT_FEATURE()
|
||||||
|
{
|
||||||
|
if (feature == null) { feature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayerPassthrough>(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override XrResult xrCreatePassthroughHTC(XrPassthroughCreateInfoHTC createInfo, out XrPassthroughHTC passthrough)
|
||||||
|
{
|
||||||
|
XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
passthrough = 0;
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if(feature)
|
||||||
|
result = feature.CreatePassthroughHTC(createInfo,out passthrough);
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public override XrResult xrDestroyPassthroughHTC(XrPassthroughHTC passthrough)
|
||||||
|
{
|
||||||
|
XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if(feature)
|
||||||
|
result = feature.DestroyPassthroughHTC(passthrough);
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GetOriginEndFrameLayerList(out List<IntPtr> layers)
|
||||||
|
{
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
layers = new List<IntPtr>();
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (feature)
|
||||||
|
feature.GetOriginEndFrameLayerList(out layers);
|
||||||
|
|
||||||
|
else
|
||||||
|
layers = new List<IntPtr>();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
public override void SubmitLayers(List<IntPtr> layers)
|
||||||
|
{
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (feature)
|
||||||
|
feature.SubmitLayers(layers);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public override XrSpace GetTrackingSpace()
|
||||||
|
{
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (feature)
|
||||||
|
return feature.GetTrackingSpace();
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override XrFrameState GetFrameState()
|
||||||
|
{
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
#if UNITY_STANDALONE
|
||||||
|
if (feature)
|
||||||
|
return feature.GetFrameState();
|
||||||
|
#endif
|
||||||
|
return new XrFrameState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 97968beb8cc921d4cbebb8e3a2bc3d6c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
public class XR_HTC_path_enumeration_defs
|
||||||
|
{
|
||||||
|
public virtual XrResult xrEnumeratePathsForInteractionProfileHTC(
|
||||||
|
ref XrPathsForInteractionProfileEnumerateInfoHTC createInfo,
|
||||||
|
UInt32 pathCapacityInput,
|
||||||
|
ref UInt32 pathCountOutput,
|
||||||
|
[In, Out] XrPath[] paths)
|
||||||
|
{
|
||||||
|
paths = null;
|
||||||
|
return XrResult.XR_ERROR_RUNTIME_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool GetUserPaths(string interactionProfileString, out XrPath[] userPaths)
|
||||||
|
{
|
||||||
|
userPaths = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public virtual bool GetInputPathsWithUserPath(string interactionProfileString, XrPath userPath, out XrPath[] inputPaths)
|
||||||
|
{
|
||||||
|
inputPaths = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string PathToString(ulong path) { return null; }
|
||||||
|
public virtual ulong StringToPath(string str) {
|
||||||
|
return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class XR_HTC_path_enumeration
|
||||||
|
{
|
||||||
|
static XR_HTC_path_enumeration_defs m_Instance = null;
|
||||||
|
public static XR_HTC_path_enumeration_defs Interop
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_Instance == null)
|
||||||
|
{
|
||||||
|
m_Instance = new XR_HTC_path_enumeration_impls();
|
||||||
|
}
|
||||||
|
return m_Instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static XrResult xrEnumeratePathsForInteractionProfileHTC(
|
||||||
|
ref XrPathsForInteractionProfileEnumerateInfoHTC createInfo,
|
||||||
|
UInt32 pathCapacityInput,
|
||||||
|
ref UInt32 pathCountOutput,
|
||||||
|
[In, Out] XrPath[] paths)
|
||||||
|
{
|
||||||
|
return Interop.xrEnumeratePathsForInteractionProfileHTC(ref createInfo,
|
||||||
|
pathCapacityInput,
|
||||||
|
ref pathCountOutput,
|
||||||
|
paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string PathToString(ulong path) { return Interop.PathToString(path); }
|
||||||
|
public static ulong StringToPath(string str)
|
||||||
|
{
|
||||||
|
return Interop.StringToPath(str); }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: be103afd6857e52418eed1936213db83
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.XR.OpenXR;
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
public class XR_HTC_path_enumeration_impls : XR_HTC_path_enumeration_defs
|
||||||
|
{
|
||||||
|
const string LOG_TAG = "VIVE.OpenXR.XR_HTC_path_enumeration_impls";
|
||||||
|
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||||
|
public XR_HTC_path_enumeration_impls() { DEBUG("XR_HTC_path_enumeration_impls()"); }
|
||||||
|
private VivePathEnumeration feature = null;
|
||||||
|
|
||||||
|
private void ASSERT_FEATURE()
|
||||||
|
{
|
||||||
|
if (feature == null) { feature = OpenXRSettings.Instance.GetFeature<VivePathEnumeration>(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override XrResult xrEnumeratePathsForInteractionProfileHTC(
|
||||||
|
ref XrPathsForInteractionProfileEnumerateInfoHTC createInfo,
|
||||||
|
UInt32 pathCapacityInput,
|
||||||
|
ref UInt32 pathCountOutput,
|
||||||
|
[In, Out] XrPath[] paths)
|
||||||
|
{
|
||||||
|
DEBUG("xrEnumeratePathsForInteractionProfileHTC");
|
||||||
|
XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
result = feature.EnumeratePathsForInteractionProfileHTC(ref createInfo, pathCapacityInput,
|
||||||
|
ref pathCountOutput, paths);
|
||||||
|
if (result != XrResult.XR_SUCCESS) { paths = null; }
|
||||||
|
}
|
||||||
|
paths = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetUserPaths(string interactionProfileString, out XrPath[] userPaths)
|
||||||
|
{
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
if (feature.GetUserPaths(interactionProfileString, out userPaths))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
userPaths = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
userPaths = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetInputPathsWithUserPath(string interactionProfileString, XrPath userPath, out XrPath[] inputPaths)
|
||||||
|
{
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
if (feature.GetInputPathsWithUserPath(interactionProfileString, userPath,out inputPaths))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inputPaths = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inputPaths = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string PathToString(ulong path)
|
||||||
|
{
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
return feature.xrPathToString(path);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public override ulong StringToPath(string str)
|
||||||
|
{
|
||||||
|
ASSERT_FEATURE();
|
||||||
|
if (feature)
|
||||||
|
return feature.xrStringToPath(str);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d0778892173385d4291570f9f47ec08d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
com.htc.upm.vive.openxr/Runtime/Toolkits/Anchor.meta
Normal file
8
com.htc.upm.vive.openxr/Runtime/Toolkits/Anchor.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ade49fa87f3cc404ea1b73cf2066c9a5
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
180
com.htc.upm.vive.openxr/Runtime/Toolkits/Anchor/AnchorManager.cs
Normal file
180
com.htc.upm.vive.openxr/Runtime/Toolkits/Anchor/AnchorManager.cs
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.XR.OpenXR;
|
||||||
|
using VIVE.OpenXR.Feature;
|
||||||
|
using VIVE.OpenXR.Anchor;
|
||||||
|
using static VIVE.OpenXR.Anchor.ViveAnchor;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.Toolkits.Anchor
|
||||||
|
{
|
||||||
|
public static class AnchorManager
|
||||||
|
{
|
||||||
|
static ViveAnchor feature = null;
|
||||||
|
static bool isSupported = false;
|
||||||
|
|
||||||
|
static void CheckFeature()
|
||||||
|
{
|
||||||
|
if (feature != null) return;
|
||||||
|
|
||||||
|
feature = OpenXRSettings.Instance.GetFeature<ViveAnchor>();
|
||||||
|
if (feature == null)
|
||||||
|
throw new NotSupportedException("ViveAnchor feature is not enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to get the extention feature instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static ViveAnchor GetFeature()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CheckFeature();
|
||||||
|
}
|
||||||
|
catch (NotSupportedException)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("ViveAnchor feature is not enabled");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the extension is supported.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsSupported()
|
||||||
|
{
|
||||||
|
if (GetFeature() == null) return false;
|
||||||
|
if (isSupported) return true;
|
||||||
|
|
||||||
|
var ret = false;
|
||||||
|
if (feature.GetProperties(out XrSystemAnchorPropertiesHTC properties) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
Debug.Log("Anchor: IsSupported() properties.supportedFeatures: " + properties.supportsAnchor);
|
||||||
|
ret = properties.supportsAnchor;
|
||||||
|
isSupported = ret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("Anchor: IsSupported() GetSystemProperties failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a spatial anchor at tracking space (Camera Rig).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pose">The related pose to the tracking space (Camera Rig)</param>
|
||||||
|
/// <returns>Anchor container</returns>
|
||||||
|
public static Anchor CreateAnchor(Pose pose, string name)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CheckFeature();
|
||||||
|
XrSpace baseSpace = feature.GetTrackingSpace();
|
||||||
|
XrSpatialAnchorCreateInfoHTC createInfo = new XrSpatialAnchorCreateInfoHTC();
|
||||||
|
createInfo.type = XrStructureType.XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC;
|
||||||
|
createInfo.poseInSpace = new XrPosef();
|
||||||
|
createInfo.poseInSpace.position = pose.position.ToOpenXRVector();
|
||||||
|
createInfo.poseInSpace.orientation = pose.rotation.ToOpenXRQuaternion();
|
||||||
|
createInfo.name.name = name;
|
||||||
|
createInfo.space = baseSpace;
|
||||||
|
|
||||||
|
if (feature.CreateSpatialAnchor(createInfo, out XrSpace anchor) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
return new Anchor(anchor, name);
|
||||||
|
}
|
||||||
|
} catch (Exception) { }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool GetSpatialAnchorName(Anchor anchor, out string name)
|
||||||
|
{
|
||||||
|
return GetSpatialAnchorName(anchor.GetXrSpace(), out name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool GetSpatialAnchorName(XrSpace anchor, out string name)
|
||||||
|
{
|
||||||
|
name = "";
|
||||||
|
CheckFeature();
|
||||||
|
XrResult ret = feature.GetSpatialAnchorName(anchor, out XrSpatialAnchorNameHTC xrName);
|
||||||
|
if (ret == XrResult.XR_SUCCESS)
|
||||||
|
name = xrName.name;
|
||||||
|
return ret == XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the XrSpace stand for current tracking space.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static XrSpace GetTrackingSpace()
|
||||||
|
{
|
||||||
|
CheckFeature();
|
||||||
|
return feature.GetTrackingSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the pose related to current tracking space. Only when position and orientation are both valid, the pose is valid.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="anchor"></param>
|
||||||
|
/// <param name="pose"></param>
|
||||||
|
/// <returns>true if both position and rotation are valid.</returns>
|
||||||
|
public static bool GetTrackingSpacePose(Anchor anchor, out Pose pose)
|
||||||
|
{
|
||||||
|
var sw = SpaceWrapper.Instance;
|
||||||
|
return anchor.GetRelatedPose(feature.GetTrackingSpace(), ViveInterceptors.Instance.GetPredictTime(), out pose);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Anchor is a named Space. It can be used to create a spatial anchor, or get the anchor's name.
|
||||||
|
/// After use it, you should call Dispose() to release the anchor.
|
||||||
|
/// </summary>
|
||||||
|
public class Anchor : VIVE.OpenXR.Feature.Space
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The anchor's name
|
||||||
|
/// </summary>
|
||||||
|
string name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The anchor's name
|
||||||
|
/// </summary>
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
name = GetSpatialAnchorName();
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Anchor(XrSpace anchor, string name) : base(anchor)
|
||||||
|
{
|
||||||
|
// Get the current tracking space.
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Anchor(Anchor other) : base(other.space)
|
||||||
|
{
|
||||||
|
// Get the current tracking space.
|
||||||
|
name = other.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the anchor's name by using this anchor's handle, instead of the anchor's Name. This will update the anchor's Name.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string GetSpatialAnchorName()
|
||||||
|
{
|
||||||
|
AnchorManager.CheckFeature();
|
||||||
|
if (AnchorManager.GetSpatialAnchorName(this, out string name))
|
||||||
|
return name;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e5dd060e5d33dc942adbd03d6b9dc9bb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b2e0dce69c4834e47b59f25575269566
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f9a059009a1d2414c94f3b9c7e46dfe9
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
public class MemoryTools
|
||||||
|
{
|
||||||
|
public static IntPtr ToIntPtr<T>(T[] array) where T : Enum
|
||||||
|
{
|
||||||
|
int size = Marshal.SizeOf(typeof(T)) * array.Length;
|
||||||
|
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||||
|
int[] intArray = new int[array.Length];
|
||||||
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
intArray[i] = (int)(object)array[i];
|
||||||
|
Marshal.Copy(intArray, 0, ptr, array.Length);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the same size raw buffer from input array.
|
||||||
|
public static IntPtr MakeRawMemory<T>(T[] refArray)
|
||||||
|
{
|
||||||
|
int size = Marshal.SizeOf(typeof(T)) * refArray.Length;
|
||||||
|
return Marshal.AllocHGlobal(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the same size raw buffer from input array.
|
||||||
|
public static void CopyFromRawMemory<T>(T[] array, IntPtr raw)
|
||||||
|
{
|
||||||
|
int step = Marshal.SizeOf(typeof(T));
|
||||||
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
array[i] = Marshal.PtrToStructure<T>(IntPtr.Add(raw, i * step));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the same size raw buffer from input array. Make sure the raw has enough size.
|
||||||
|
public static void CopyToRawMemory<T>(IntPtr raw, T[] array)
|
||||||
|
{
|
||||||
|
int step = Marshal.SizeOf(typeof(T));
|
||||||
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
Marshal.StructureToPtr<T>(array[i], IntPtr.Add(raw, i * step), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReleaseRawMemory(IntPtr ptr)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9a887cb158a37cf45b17458a4f27d7ee
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,381 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.XR.OpenXR;
|
||||||
|
using VIVE.OpenXR.PlaneDetection;
|
||||||
|
using static VIVE.OpenXR.PlaneDetection.VivePlaneDetection;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.Toolkits.PlaneDetection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The detected plane's data.
|
||||||
|
/// See <see cref="VivePlaneDetection.XrPlaneDetectorLocationEXT"/>
|
||||||
|
/// </summary>
|
||||||
|
public class PlaneDetectorLocation
|
||||||
|
{
|
||||||
|
public ulong planeId;
|
||||||
|
public XrSpaceLocationFlags locationFlags;
|
||||||
|
public Pose pose;
|
||||||
|
public Vector3 size; // Only width(X) and height(Y) are valid, Z is always 0.
|
||||||
|
public XrPlaneDetectorOrientationEXT orientation;
|
||||||
|
public XrPlaneDetectorSemanticTypeEXT semanticType;
|
||||||
|
public uint polygonBufferCount;
|
||||||
|
public XrPlaneDetectorLocationEXT locationRaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The information for creating Mesh.
|
||||||
|
/// Plane's normal is facing +Z in Unity's coordination.
|
||||||
|
/// </summary>
|
||||||
|
public class Plane
|
||||||
|
{
|
||||||
|
public Vector3 scale; // Only width(X) and height(Y) are valid, Z is always 1.
|
||||||
|
public Vector3 center; // Should always be Vector3.Zero.
|
||||||
|
public Vector3[] verticesRaw; // The original vertices from <see cref="XrPlaneDetectorPolygonBufferEXT"/>
|
||||||
|
public Vector3[] verticesGenerated; // generated vertices for creating Mesh.
|
||||||
|
public Vector2[] uvsGenerated;
|
||||||
|
public int[] indicesGenerated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// According to the input vertices, calculate the rectangle, and create the plane.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vertices">The vertices from <see cref="XrPlaneDetectorPolygonBufferEXT"/></param>
|
||||||
|
public static Plane CreateFromVertices(Vector2[] vertices)
|
||||||
|
{
|
||||||
|
// Assume the polygon is a rectangle.
|
||||||
|
if (vertices.Length != 4)
|
||||||
|
return null;
|
||||||
|
// Check the size from vertices.
|
||||||
|
Vector2 min, max;
|
||||||
|
min = max = new Vector2(vertices[0].x, vertices[0].y);
|
||||||
|
for (int i = 1; i < vertices.Length; i++)
|
||||||
|
{
|
||||||
|
min.x = Mathf.Min(min.x, vertices[i].x);
|
||||||
|
min.y = Mathf.Min(min.y, vertices[i].y);
|
||||||
|
max.x = Mathf.Max(max.x, vertices[i].x);
|
||||||
|
max.y = Mathf.Max(max.y, vertices[i].y);
|
||||||
|
}
|
||||||
|
var verticesRaw = new Vector3[vertices.Length];
|
||||||
|
for (int i = 0; i < vertices.Length; i++)
|
||||||
|
verticesRaw[i] = new Vector3(vertices[i].x, 0, vertices[i].y);
|
||||||
|
|
||||||
|
var verticesGenerated = new Vector3[4];
|
||||||
|
verticesGenerated[0] = new Vector3(min.x, min.y, 0);
|
||||||
|
verticesGenerated[1] = new Vector3(max.x, min.y, 0);
|
||||||
|
verticesGenerated[2] = new Vector3(min.x, max.y, 0);
|
||||||
|
verticesGenerated[3] = new Vector3(max.x, max.y, 0);
|
||||||
|
|
||||||
|
var indicesGenerated = new int[] { 0, 3, 2, 0, 1, 3 };
|
||||||
|
|
||||||
|
var uvsGenerated = new Vector2[4];
|
||||||
|
uvsGenerated[0] = new Vector2(0, 0);
|
||||||
|
uvsGenerated[1] = new Vector2(1, 0);
|
||||||
|
uvsGenerated[2] = new Vector2(0, 1);
|
||||||
|
uvsGenerated[3] = new Vector2(1, 1);
|
||||||
|
|
||||||
|
return new Plane()
|
||||||
|
{
|
||||||
|
scale = max - min,
|
||||||
|
center = (max + min) / 2,
|
||||||
|
verticesRaw = verticesRaw,
|
||||||
|
verticesGenerated = verticesGenerated,
|
||||||
|
indicesGenerated = indicesGenerated,
|
||||||
|
uvsGenerated = uvsGenerated
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The PlaneDetector is created by <see cref="PlaneDetectionManager.CreatePlaneDetector" />.
|
||||||
|
/// </summary>
|
||||||
|
public class PlaneDetector
|
||||||
|
{
|
||||||
|
IntPtr planeDetector = IntPtr.Zero;
|
||||||
|
VivePlaneDetection feature = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should not create PlaneDetector directly. Use <see cref="PlaneDetectionManager.CreatePlaneDetector" /> instead.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pd">The native handle of plane detector</param>
|
||||||
|
/// <param name="f">the feature</param>
|
||||||
|
internal PlaneDetector(IntPtr pd, VivePlaneDetection f)
|
||||||
|
{
|
||||||
|
feature = f;
|
||||||
|
planeDetector = pd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the raw plane detector handle. See <see cref="VivePlaneDetection.CreatePlaneDetector"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The raw plane detector handle</returns>
|
||||||
|
public IntPtr GetDetectorRaw()
|
||||||
|
{
|
||||||
|
return planeDetector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begin detect plane. In VIVE's implementation, planes are predefined in Room Setup.
|
||||||
|
/// However you have to call this function to get the plane information.
|
||||||
|
/// See <see cref="VivePlaneDetection.BeginPlaneDetection"/>
|
||||||
|
/// </summary>
|
||||||
|
public XrResult BeginPlaneDetection()
|
||||||
|
{
|
||||||
|
if (feature == null)
|
||||||
|
return XrResult.XR_ERROR_FEATURE_UNSUPPORTED;
|
||||||
|
Debug.Log("BeginPlaneDetection()");
|
||||||
|
var beginInfo = feature.MakeGetAllXrPlaneDetectorBeginInfoEXT();
|
||||||
|
return feature.BeginPlaneDetection(planeDetector, beginInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the state of plane detection.
|
||||||
|
/// See <see cref="VivePlaneDetection.GetPlaneDetectionState"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrPlaneDetectionStateEXT GetPlaneDetectionState()
|
||||||
|
{
|
||||||
|
if (feature == null)
|
||||||
|
return XrPlaneDetectionStateEXT.NONE_EXT;
|
||||||
|
Debug.Log("GetPlaneDetectionState()");
|
||||||
|
XrPlaneDetectionStateEXT state = XrPlaneDetectionStateEXT.NONE_EXT;
|
||||||
|
feature.GetPlaneDetectionState(planeDetector, ref state);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get result of plane detection.
|
||||||
|
/// See <see cref="VivePlaneDetection.GetPlaneDetections"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="locations">The detected planes.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrResult GetPlaneDetections(out List<PlaneDetectorLocation> locations)
|
||||||
|
{
|
||||||
|
locations = null;
|
||||||
|
if (feature == null)
|
||||||
|
return XrResult.XR_ERROR_FEATURE_UNSUPPORTED;
|
||||||
|
Debug.Log("GetPlaneDetections()");
|
||||||
|
XrPlaneDetectorGetInfoEXT info = new XrPlaneDetectorGetInfoEXT
|
||||||
|
{
|
||||||
|
type = XrStructureType.XR_TYPE_PLANE_DETECTOR_GET_INFO_EXT,
|
||||||
|
baseSpace = feature.GetTrackingSpace(),
|
||||||
|
time = feature.GetPredictTime(),
|
||||||
|
};
|
||||||
|
XrPlaneDetectorLocationsEXT locationsRaw = new XrPlaneDetectorLocationsEXT
|
||||||
|
{
|
||||||
|
type = XrStructureType.XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT,
|
||||||
|
planeLocationCapacityInput = 0,
|
||||||
|
planeLocationCountOutput = 0,
|
||||||
|
planeLocations = IntPtr.Zero,
|
||||||
|
};
|
||||||
|
var ret = feature.GetPlaneDetections(planeDetector, ref info, ref locationsRaw);
|
||||||
|
if (ret != XrResult.XR_SUCCESS || locationsRaw.planeLocationCountOutput == 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
Debug.Log("GetPlaneDetections() locations.planeLocationCountOutput: " + locationsRaw.planeLocationCountOutput);
|
||||||
|
var locationsArray = new XrPlaneDetectorLocationEXT[locationsRaw.planeLocationCountOutput];
|
||||||
|
var locationsPtr = MemoryTools.MakeRawMemory(locationsArray);
|
||||||
|
locationsRaw.planeLocationCapacityInput = locationsRaw.planeLocationCountOutput;
|
||||||
|
locationsRaw.planeLocationCountOutput = 0;
|
||||||
|
locationsRaw.planeLocations = locationsPtr;
|
||||||
|
|
||||||
|
ret = feature.GetPlaneDetections(planeDetector, ref info, ref locationsRaw);
|
||||||
|
if (ret != XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
MemoryTools.ReleaseRawMemory(locationsPtr);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
MemoryTools.CopyFromRawMemory(locationsArray, locationsPtr);
|
||||||
|
|
||||||
|
locations = new List<PlaneDetectorLocation>();
|
||||||
|
|
||||||
|
// The plane's neutral pose is horizontal, and not like the plane pose in unity which is vertical.
|
||||||
|
// Therefore, we wil perform a rotation to convert from the OpenXR's forward to unity's forward.
|
||||||
|
// In Unity, the rotation applied order is in ZXY order.
|
||||||
|
Quaternion forward = Quaternion.Euler(-90, 180, 0);
|
||||||
|
for (int i = 0; i < locationsRaw.planeLocationCountOutput; i++)
|
||||||
|
{
|
||||||
|
XrPlaneDetectorLocationEXT location = locationsArray[i];
|
||||||
|
PlaneDetectorLocation pdl = new PlaneDetectorLocation
|
||||||
|
{
|
||||||
|
planeId = location.planeId,
|
||||||
|
locationFlags = location.locationFlags,
|
||||||
|
pose = new Pose(OpenXRHelper.ToUnityVector(location.pose.position), OpenXRHelper.ToUnityQuaternion(location.pose.orientation) * forward),
|
||||||
|
// Because the pose is converted, we will apply extent to X and Y.
|
||||||
|
size = new Vector3(location.extents.width, location.extents.height, 0),
|
||||||
|
orientation = location.orientation,
|
||||||
|
semanticType = location.semanticType,
|
||||||
|
polygonBufferCount = location.polygonBufferCount,
|
||||||
|
locationRaw = location,
|
||||||
|
};
|
||||||
|
locations.Add(pdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryTools.ReleaseRawMemory(locationsPtr);
|
||||||
|
|
||||||
|
for (int i = 0; i < locationsRaw.planeLocationCountOutput; i++)
|
||||||
|
{
|
||||||
|
var location = locations[i];
|
||||||
|
Debug.Log("GetPlaneDetections() location.planeId: " + location.planeId);
|
||||||
|
Debug.Log("GetPlaneDetections() location.locationFlags: " + location.locationFlags);
|
||||||
|
Debug.Log("GetPlaneDetections() location.pose.position: " + location.pose.position);
|
||||||
|
Debug.Log("GetPlaneDetections() location.pose.rotation: " + location.pose.rotation);
|
||||||
|
Debug.Log("GetPlaneDetections() location.pose.rotation.eulerAngles: " + location.pose.rotation.eulerAngles);
|
||||||
|
var rot = location.locationRaw.pose.orientation;
|
||||||
|
Debug.Log($"GetPlaneDetections() locationRaw.pose.rotation: {rot.x}, {rot.y}, {rot.z}, {rot.w}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the vertices of the plane from extension. Because there is no triangle
|
||||||
|
/// information from extension, it is hard to generate a mesh from only these vertices.
|
||||||
|
/// However VIVE only have rectangle plane. In this function, it will return the
|
||||||
|
/// <see cref="Plane"/> class which contains generated information for creating Mesh.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="planeId">The planeId from <see cref="PlaneDetectorLocation"/></param>
|
||||||
|
/// <returns>The information for creating Mesh.</returns>
|
||||||
|
public Plane GetPlane(ulong planeId)
|
||||||
|
{
|
||||||
|
if (feature == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
XrPlaneDetectorPolygonBufferEXT polygonBuffer = new XrPlaneDetectorPolygonBufferEXT
|
||||||
|
{
|
||||||
|
type = XrStructureType.XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT,
|
||||||
|
vertexCapacityInput = 0,
|
||||||
|
vertexCountOutput = 0,
|
||||||
|
vertices = IntPtr.Zero,
|
||||||
|
};
|
||||||
|
|
||||||
|
var ret = feature.GetPlanePolygonBuffer(planeDetector, planeId, 0, ref polygonBuffer);
|
||||||
|
Debug.Log("GetPlane() polygonBuffer.vertexCountOutput: " + polygonBuffer.vertexCountOutput);
|
||||||
|
if (ret != XrResult.XR_SUCCESS || polygonBuffer.vertexCountOutput == 0)
|
||||||
|
return null;
|
||||||
|
var verticesArray = new Vector2[polygonBuffer.vertexCountOutput];
|
||||||
|
var verticesPtr = MemoryTools.MakeRawMemory(verticesArray);
|
||||||
|
polygonBuffer.vertexCapacityInput = polygonBuffer.vertexCountOutput;
|
||||||
|
polygonBuffer.vertexCountOutput = 0;
|
||||||
|
polygonBuffer.vertices = verticesPtr;
|
||||||
|
if (feature.GetPlanePolygonBuffer(planeDetector, planeId, 0, ref polygonBuffer) != XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
MemoryTools.ReleaseRawMemory(verticesPtr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
MemoryTools.CopyFromRawMemory(verticesArray, verticesPtr);
|
||||||
|
MemoryTools.ReleaseRawMemory(verticesPtr);
|
||||||
|
|
||||||
|
for (int j = 0; j < verticesArray.Length; j++)
|
||||||
|
{
|
||||||
|
var v = verticesArray[j];
|
||||||
|
Debug.Log($"GetPlane() verticesArray[{j}]: ({v.x}, {v.y})");
|
||||||
|
}
|
||||||
|
return Plane.CreateFromVertices(verticesArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PlaneDetectionManager
|
||||||
|
{
|
||||||
|
static VivePlaneDetection feature = null;
|
||||||
|
static bool isSupported = false;
|
||||||
|
|
||||||
|
static void CheckFeature()
|
||||||
|
{
|
||||||
|
if (feature != null) return;
|
||||||
|
feature = OpenXRSettings.Instance.GetFeature<VivePlaneDetection>();
|
||||||
|
if (feature == null)
|
||||||
|
throw new NotSupportedException("PlaneDetection feature is not enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to get the extention feature instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static VivePlaneDetection GetFeature()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CheckFeature();
|
||||||
|
}
|
||||||
|
catch (NotSupportedException)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("PlaneDetection feature is not enabled");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the extension is supported.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsSupported()
|
||||||
|
{
|
||||||
|
if (GetFeature() == null) return false;
|
||||||
|
if (isSupported) return true;
|
||||||
|
if (feature == null) return false;
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (feature.GetProperties(out var properties) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
Debug.Log("PlaneDetection: IsSupported() properties.supportedFeatures: " + properties.supportedFeatures);
|
||||||
|
ret = (properties.supportedFeatures & CAPABILITY_PLANE_DETECTION_BIT_EXT) > 0;
|
||||||
|
isSupported = ret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("PlaneDetection: IsSupported() GetSystemProperties failed.");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is a helper function. Currently only one createInfo is available. Developepr should create their own
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static XrPlaneDetectorCreateInfoEXT MakeXrPlaneDetectorCreateInfoEXT()
|
||||||
|
{
|
||||||
|
return new XrPlaneDetectorCreateInfoEXT
|
||||||
|
{
|
||||||
|
type = XrStructureType.XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT,
|
||||||
|
flags = XR_PLANE_DETECTOR_ENABLE_CONTOUR_BIT_EXT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plane detector is a session of detect plane. You don't need to create multiple plane detector in VIVE's implemention. You need destroy it.
|
||||||
|
/// Should call <see cref="IsSupported"/> first to check if the feature is supported.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>PlaneDetector's handle</returns>
|
||||||
|
public static PlaneDetector CreatePlaneDetector()
|
||||||
|
{
|
||||||
|
CheckFeature();
|
||||||
|
if (feature == null)
|
||||||
|
return null;
|
||||||
|
if (IsSupported() == false)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var createInfo = MakeXrPlaneDetectorCreateInfoEXT();
|
||||||
|
var ret = feature.CreatePlaneDetector(createInfo, out var planeDetector);
|
||||||
|
if (ret != XrResult.XR_SUCCESS)
|
||||||
|
return null;
|
||||||
|
return new PlaneDetector(planeDetector, feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destroy the plane detector to release resource.
|
||||||
|
/// </summary>
|
||||||
|
public static void DestroyPlaneDetector(PlaneDetector pd)
|
||||||
|
{
|
||||||
|
if (pd == null)
|
||||||
|
return;
|
||||||
|
CheckFeature();
|
||||||
|
if (feature == null)
|
||||||
|
return;
|
||||||
|
feature.DestroyPlaneDetector(pd.GetDetectorRaw());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7f0a3b1ff9cb22f4293ff346f84a541e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fa3c636a6aff8234b9c9e019b5396109
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ff1e9b9f321387c4e86e6a5fa6cd65c9
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// "Wave SDK
|
||||||
|
// © 2020 HTC Corporation. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Unless otherwise required by copyright law and practice,
|
||||||
|
// upon the execution of HTC SDK license agreement,
|
||||||
|
// HTC grants you access to and use of the Wave SDK(s).
|
||||||
|
// You shall fully comply with all of HTC’s SDK license agreement terms and
|
||||||
|
// conditions signed by you and all SDK and API requirements,
|
||||||
|
// specifications, and documentation provided by HTC to You."
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
namespace VIVE.OpenXR.Toolkits.RealisticHandInteraction
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(CustomGrabPose))]
|
||||||
|
public class CustomGrabPoseEditor : UnityEditor.Editor
|
||||||
|
{
|
||||||
|
private CustomGrabPose m_GrabPoseDesigner;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
m_GrabPoseDesigner = target as CustomGrabPose;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
base.OnInspectorGUI();
|
||||||
|
GUILayout.Space(10f);
|
||||||
|
ShowGrabPosesMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowGrabPosesMenu()
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("Save HandGrab Pose"))
|
||||||
|
{
|
||||||
|
m_GrabPoseDesigner.FindNearInteractable();
|
||||||
|
m_GrabPoseDesigner.SavePoseWithCandidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c01f88bc88bb27849a723888721c96f4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,382 @@
|
|||||||
|
// "Wave SDK
|
||||||
|
// © 2020 HTC Corporation. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Unless otherwise required by copyright law and practice,
|
||||||
|
// upon the execution of HTC SDK license agreement,
|
||||||
|
// HTC grants you access to and use of the Wave SDK(s).
|
||||||
|
// You shall fully comply with all of HTC’s SDK license agreement terms and
|
||||||
|
// conditions signed by you and all SDK and API requirements,
|
||||||
|
// specifications, and documentation provided by HTC to You."
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditorInternal;
|
||||||
|
namespace VIVE.OpenXR.Toolkits.RealisticHandInteraction
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(HandGrabInteractable))]
|
||||||
|
public class HandGrabInteractableEditor : UnityEditor.Editor
|
||||||
|
{
|
||||||
|
private static HandGrabInteractable activeGrabbable = null;
|
||||||
|
private HandGrabInteractable handGrabbable = null;
|
||||||
|
private SerializedProperty m_IsGrabbable, m_FingerRequirement, m_Rigidbody, m_GrabPoses, m_ShowAllIndicator, m_OnBeginGrabbed, m_OnEndGrabbed,
|
||||||
|
m_OneHandContraintMovement, m_PreviewIndex, grabPoseName, gestureThumbPose, gestureIndexPose, gestureMiddlePose, gestureRingPose, gesturePinkyPose,
|
||||||
|
recordedGrabRotations, isLeft, enableIndicator, autoIndicator, indicatorObject, grabOffset;
|
||||||
|
private ReorderableList grabPoseList;
|
||||||
|
private bool showGrabPoses = false;
|
||||||
|
private bool showConstraint = false;
|
||||||
|
private bool showEvent = false;
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
handGrabbable = target as HandGrabInteractable;
|
||||||
|
|
||||||
|
m_IsGrabbable = serializedObject.FindProperty("m_IsGrabbable");
|
||||||
|
m_FingerRequirement = serializedObject.FindProperty("m_FingerRequirement");
|
||||||
|
m_Rigidbody = serializedObject.FindProperty("m_Rigidbody");
|
||||||
|
m_GrabPoses = serializedObject.FindProperty("m_GrabPoses");
|
||||||
|
m_ShowAllIndicator = serializedObject.FindProperty("m_ShowAllIndicator");
|
||||||
|
m_OnBeginGrabbed = serializedObject.FindProperty("m_OnBeginGrabbed");
|
||||||
|
m_OnEndGrabbed = serializedObject.FindProperty("m_OnEndGrabbed");
|
||||||
|
m_OneHandContraintMovement = serializedObject.FindProperty("m_OneHandContraintMovement");
|
||||||
|
m_PreviewIndex = serializedObject.FindProperty("m_PreviewIndex");
|
||||||
|
|
||||||
|
#region ReorderableList
|
||||||
|
grabPoseList = new ReorderableList(serializedObject, m_GrabPoses, true, true, true, true);
|
||||||
|
|
||||||
|
grabPoseList.drawHeaderCallback = (Rect rect) =>
|
||||||
|
{
|
||||||
|
EditorGUI.LabelField(rect, "Grab Pose List");
|
||||||
|
};
|
||||||
|
|
||||||
|
grabPoseList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||||
|
{
|
||||||
|
if (!UpdateGrabPose(m_GrabPoses.GetArrayElementAtIndex(index))) { return; }
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(grabPoseName.stringValue))
|
||||||
|
{
|
||||||
|
grabPoseName.stringValue = $"Grab Pose {index + 1}";
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect elementRect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight);
|
||||||
|
grabPoseName.stringValue = EditorGUI.TextField(elementRect, grabPoseName.stringValue);
|
||||||
|
|
||||||
|
DrawGrabGesture(ref elementRect);
|
||||||
|
DrawHandedness(ref elementRect);
|
||||||
|
DrawIndicator(ref elementRect);
|
||||||
|
DrawMirrorButton(ref elementRect);
|
||||||
|
DrawPoseOffset(ref elementRect);
|
||||||
|
DrawFineTune(ref elementRect, index);
|
||||||
|
};
|
||||||
|
|
||||||
|
grabPoseList.elementHeightCallback = (int index) =>
|
||||||
|
{
|
||||||
|
if (!UpdateGrabPose(m_GrabPoses.GetArrayElementAtIndex(index))) { return EditorGUIUtility.singleLineHeight; }
|
||||||
|
|
||||||
|
// Including Title, Handness, Show Indicator, Mirror Pose, Position, Rotation, Fine Tune
|
||||||
|
int minHeight = 7;
|
||||||
|
// To Show GrabGesture
|
||||||
|
if (recordedGrabRotations.arraySize == 0)
|
||||||
|
{
|
||||||
|
minHeight += 5;
|
||||||
|
}
|
||||||
|
if (enableIndicator.boolValue)
|
||||||
|
{
|
||||||
|
// To Show Auto Indicator
|
||||||
|
minHeight += 1;
|
||||||
|
// To Show Indicator Gameobject
|
||||||
|
if (!autoIndicator.boolValue)
|
||||||
|
{
|
||||||
|
minHeight += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EditorGUIUtility.singleLineHeight * minHeight + EditorGUIUtility.standardVerticalSpacing * minHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
grabPoseList.onAddCallback = (ReorderableList list) =>
|
||||||
|
{
|
||||||
|
m_GrabPoses.arraySize++;
|
||||||
|
if (UpdateGrabPose(m_GrabPoses.GetArrayElementAtIndex(list.count - 1)))
|
||||||
|
{
|
||||||
|
grabPoseName.stringValue = $"Grab Pose {list.count}";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
serializedObject.Update();
|
||||||
|
EditorGUILayout.PropertyField(m_Rigidbody);
|
||||||
|
EditorGUILayout.PropertyField(m_IsGrabbable);
|
||||||
|
EditorGUILayout.PropertyField(m_FingerRequirement);
|
||||||
|
showGrabPoses = EditorGUILayout.Foldout(showGrabPoses, "Grab Pose Settings");
|
||||||
|
if (showGrabPoses)
|
||||||
|
{
|
||||||
|
if (m_GrabPoses.arraySize == 0)
|
||||||
|
{
|
||||||
|
grabPoseList.elementHeight = EditorGUIUtility.singleLineHeight;
|
||||||
|
}
|
||||||
|
grabPoseList.DoLayoutList();
|
||||||
|
|
||||||
|
bool isToggle = EditorGUILayout.Toggle("Show All Indicator", m_ShowAllIndicator.boolValue);
|
||||||
|
if (isToggle != m_ShowAllIndicator.boolValue)
|
||||||
|
{
|
||||||
|
m_ShowAllIndicator.boolValue = isToggle;
|
||||||
|
for (int i = 0; i < m_GrabPoses.arraySize; i++)
|
||||||
|
{
|
||||||
|
if (UpdateGrabPose(m_GrabPoses.GetArrayElementAtIndex(i)))
|
||||||
|
{
|
||||||
|
enableIndicator.boolValue = m_ShowAllIndicator.boolValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showEvent = EditorGUILayout.Foldout(showEvent, "Grabbed Event");
|
||||||
|
if (showEvent)
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(m_OnBeginGrabbed);
|
||||||
|
EditorGUILayout.PropertyField(m_OnEndGrabbed);
|
||||||
|
}
|
||||||
|
|
||||||
|
showConstraint = EditorGUILayout.Foldout(showConstraint, "Constraint Movement (Optional)");
|
||||||
|
if (showConstraint)
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(m_OneHandContraintMovement);
|
||||||
|
}
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool UpdateGrabPose(SerializedProperty grabPose)
|
||||||
|
{
|
||||||
|
SerializedProperty indicator = grabPose.FindPropertyRelative("indicator");
|
||||||
|
if (grabPose == null || indicator == null) { return false; }
|
||||||
|
|
||||||
|
grabPoseName = grabPose.FindPropertyRelative("grabPoseName");
|
||||||
|
gestureThumbPose = grabPose.FindPropertyRelative("handGrabGesture.thumbPose");
|
||||||
|
gestureIndexPose = grabPose.FindPropertyRelative("handGrabGesture.indexPose");
|
||||||
|
gestureMiddlePose = grabPose.FindPropertyRelative("handGrabGesture.middlePose");
|
||||||
|
gestureRingPose = grabPose.FindPropertyRelative("handGrabGesture.ringPose");
|
||||||
|
gesturePinkyPose = grabPose.FindPropertyRelative("handGrabGesture.pinkyPose");
|
||||||
|
recordedGrabRotations = grabPose.FindPropertyRelative("recordedGrabRotations");
|
||||||
|
isLeft = grabPose.FindPropertyRelative("isLeft");
|
||||||
|
enableIndicator = indicator.FindPropertyRelative("enableIndicator");
|
||||||
|
autoIndicator = indicator.FindPropertyRelative("autoIndicator");
|
||||||
|
indicatorObject = indicator.FindPropertyRelative("target");
|
||||||
|
grabOffset = grabPose.FindPropertyRelative("grabOffset");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddElementHeight(ref Rect rect)
|
||||||
|
{
|
||||||
|
rect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawGrabGesture(ref Rect rect)
|
||||||
|
{
|
||||||
|
if (recordedGrabRotations.arraySize == 0)
|
||||||
|
{
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
EditorGUI.PropertyField(rect, gestureThumbPose);
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
EditorGUI.PropertyField(rect, gestureIndexPose);
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
EditorGUI.PropertyField(rect, gestureMiddlePose);
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
EditorGUI.PropertyField(rect, gestureRingPose);
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
EditorGUI.PropertyField(rect, gesturePinkyPose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawHandedness(ref Rect rect)
|
||||||
|
{
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
bool isToggle = EditorGUI.Toggle(rect, "Is Left", isLeft.boolValue);
|
||||||
|
if (isToggle != isLeft.boolValue)
|
||||||
|
{
|
||||||
|
isLeft.boolValue = isToggle;
|
||||||
|
SwitchRotations(ref recordedGrabRotations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawIndicator(ref Rect rect)
|
||||||
|
{
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
enableIndicator.boolValue = EditorGUI.Toggle(rect, "Show Indicator", enableIndicator.boolValue);
|
||||||
|
if (enableIndicator.boolValue)
|
||||||
|
{
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
autoIndicator.boolValue = EditorGUI.Toggle(rect, "Auto Generator Indicator", autoIndicator.boolValue);
|
||||||
|
if (!autoIndicator.boolValue)
|
||||||
|
{
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
indicatorObject.objectReferenceValue = (GameObject)EditorGUI.ObjectField(rect, "Indicator", (GameObject)indicatorObject.objectReferenceValue, typeof(GameObject), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ShowAllIndicator.boolValue = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawMirrorButton(ref Rect rect)
|
||||||
|
{
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
Rect labelRect = new Rect(rect.x, rect.y, EditorGUIUtility.labelWidth, rect.height);
|
||||||
|
EditorGUI.PrefixLabel(labelRect, GUIUtility.GetControlID(FocusType.Passive), new GUIContent("Mirror Pose"));
|
||||||
|
|
||||||
|
Rect mirrorXRect = new Rect(rect.x + EditorGUIUtility.labelWidth + EditorGUIUtility.standardVerticalSpacing, rect.y,
|
||||||
|
(rect.width - EditorGUIUtility.labelWidth - EditorGUIUtility.standardVerticalSpacing * 4) / 3, rect.height);
|
||||||
|
Rect mirrorYRect = new Rect(mirrorXRect.x + mirrorXRect.width + EditorGUIUtility.standardVerticalSpacing, rect.y, mirrorXRect.width, rect.height);
|
||||||
|
Rect mirrorZRect = new Rect(mirrorYRect.x + mirrorYRect.width + EditorGUIUtility.standardVerticalSpacing, rect.y, mirrorYRect.width, rect.height);
|
||||||
|
if (GUI.Button(mirrorXRect, "Align X axis"))
|
||||||
|
{
|
||||||
|
MirrorPose(ref grabOffset, Vector3.right);
|
||||||
|
}
|
||||||
|
if (GUI.Button(mirrorYRect, "Align Y axis"))
|
||||||
|
{
|
||||||
|
MirrorPose(ref grabOffset, Vector3.up);
|
||||||
|
}
|
||||||
|
if (GUI.Button(mirrorZRect, "Align Z axis"))
|
||||||
|
{
|
||||||
|
MirrorPose(ref grabOffset, Vector3.forward);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawPoseOffset(ref Rect rect)
|
||||||
|
{
|
||||||
|
SerializedProperty srcPos = grabOffset.FindPropertyRelative("sourcePosition");
|
||||||
|
SerializedProperty srcRot = grabOffset.FindPropertyRelative("sourceRotation");
|
||||||
|
SerializedProperty tgtPos = grabOffset.FindPropertyRelative("targetPosition");
|
||||||
|
SerializedProperty tgtRot = grabOffset.FindPropertyRelative("targetRotation");
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
EditorGUI.Vector3Field(rect, "Position Offset", tgtPos.vector3Value - srcPos.vector3Value);
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
Vector3 rotEulerAngles = (Quaternion.Inverse(srcRot.quaternionValue) * tgtRot.quaternionValue).eulerAngles;
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
if (rotEulerAngles[i] > 180)
|
||||||
|
{
|
||||||
|
rotEulerAngles[i] = 360.0f - rotEulerAngles[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EditorGUI.Vector3Field(rect, "Rotation Offset", rotEulerAngles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawFineTune(ref Rect rect, int index)
|
||||||
|
{
|
||||||
|
AddElementHeight(ref rect);
|
||||||
|
Rect labelRect = new Rect(rect.x, rect.y, EditorGUIUtility.labelWidth, rect.height);
|
||||||
|
EditorGUI.PrefixLabel(labelRect, GUIUtility.GetControlID(FocusType.Passive), new GUIContent("Fine Tune"));
|
||||||
|
|
||||||
|
Rect previewRect = new Rect(rect.x + EditorGUIUtility.labelWidth + EditorGUIUtility.standardVerticalSpacing, rect.y,
|
||||||
|
(rect.width - EditorGUIUtility.labelWidth - EditorGUIUtility.standardVerticalSpacing * 4) / 2, rect.height);
|
||||||
|
Rect updateRect = new Rect(previewRect.x + previewRect.width + EditorGUIUtility.standardVerticalSpacing, rect.y, previewRect.width, rect.height);
|
||||||
|
if (GUI.Button(previewRect, "Preview Grab Pose") && Application.isPlaying)
|
||||||
|
{
|
||||||
|
activeGrabbable = handGrabbable;
|
||||||
|
m_PreviewIndex.intValue = index;
|
||||||
|
ShowMeshHandPose();
|
||||||
|
}
|
||||||
|
GUI.enabled = activeGrabbable == handGrabbable && m_PreviewIndex.intValue == index;
|
||||||
|
if (GUI.Button(updateRect, "Update Grab Pose"))
|
||||||
|
{
|
||||||
|
UpdateGrabPose();
|
||||||
|
}
|
||||||
|
GUI.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the rotation of joints of the current hand into those of another hand.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rotations">Rotation of joints of the current hand.</param>
|
||||||
|
private void SwitchRotations(ref SerializedProperty rotations)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < rotations.arraySize; i++)
|
||||||
|
{
|
||||||
|
Quaternion rotation = rotations.GetArrayElementAtIndex(i).quaternionValue;
|
||||||
|
Quaternion newRotation = Quaternion.Euler(rotation.eulerAngles.x, -rotation.eulerAngles.y, -rotation.eulerAngles.z);
|
||||||
|
rotations.GetArrayElementAtIndex(i).quaternionValue = newRotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mirrors the pose properties (position and rotation) of a serialized object along a specified mirror axis.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pose">The serialized property representing the pose to be mirrored.</param>
|
||||||
|
/// <param name="mirrorAxis">The axis along which the mirroring should occur.</param>
|
||||||
|
private void MirrorPose(ref SerializedProperty pose, Vector3 mirrorAxis)
|
||||||
|
{
|
||||||
|
Vector3 sourcePosition = grabOffset.FindPropertyRelative("sourcePosition").vector3Value;
|
||||||
|
Quaternion sourceRotation = grabOffset.FindPropertyRelative("sourceRotation").quaternionValue;
|
||||||
|
Vector3 targetPosition = grabOffset.FindPropertyRelative("targetPosition").vector3Value;
|
||||||
|
Quaternion targetRotation = grabOffset.FindPropertyRelative("targetRotation").quaternionValue;
|
||||||
|
Vector3 reflectNormal = targetRotation * mirrorAxis;
|
||||||
|
|
||||||
|
Vector3 diffPos = sourcePosition - targetPosition;
|
||||||
|
Vector3 mirrorPosition = targetPosition + Vector3.Reflect(diffPos, reflectNormal);
|
||||||
|
pose.FindPropertyRelative("sourcePosition").vector3Value = mirrorPosition;
|
||||||
|
|
||||||
|
Vector3 sourceForward = sourceRotation * Vector3.forward;
|
||||||
|
Vector3 sourceUp = sourceRotation * Vector3.up;
|
||||||
|
Quaternion mirroredRotation = Quaternion.LookRotation(Vector3.Reflect(sourceForward, reflectNormal), Vector3.Reflect(sourceUp, reflectNormal));
|
||||||
|
pose.FindPropertyRelative("sourceRotation").quaternionValue = mirroredRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtain the MeshHand and set its position and rotation based on the grabOffset of grabpose.
|
||||||
|
/// </summary>
|
||||||
|
private void ShowMeshHandPose()
|
||||||
|
{
|
||||||
|
HandPose handPose = HandPoseProvider.GetHandPose(isLeft.boolValue ? HandPoseType.MESH_LEFT : HandPoseType.MESH_RIGHT);
|
||||||
|
if (handPose != null && handPose is MeshHandPose meshHandPose)
|
||||||
|
{
|
||||||
|
GrabOffset grabOffsetObj = handGrabbable.grabPoses[m_PreviewIndex.intValue].grabOffset;
|
||||||
|
Quaternion handRot = handGrabbable.transform.rotation * Quaternion.Inverse(grabOffsetObj.rotOffset);
|
||||||
|
Quaternion handRotDiff = handRot * Quaternion.Inverse(grabOffsetObj.sourceRotation);
|
||||||
|
Vector3 handPos = handGrabbable.transform.position - handRotDiff * grabOffsetObj.posOffset;
|
||||||
|
meshHandPose.SetJointPose(JointType.Wrist, new Pose(handPos, handRot));
|
||||||
|
|
||||||
|
foreach (JointType joint in Enum.GetValues(typeof(JointType)))
|
||||||
|
{
|
||||||
|
if (joint == JointType.Wrist || joint == JointType.Count) { continue; }
|
||||||
|
|
||||||
|
meshHandPose.GetPosition(joint, out Vector3 pos, local: true);
|
||||||
|
Quaternion rot = recordedGrabRotations.GetArrayElementAtIndex((int)joint).quaternionValue;
|
||||||
|
meshHandPose.SetJointPose(joint, new Pose(pos, rot), local: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the grabpose based on position and rotation of the MeshHand and Object.
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateGrabPose()
|
||||||
|
{
|
||||||
|
HandPose handPose = HandPoseProvider.GetHandPose(isLeft.boolValue ? HandPoseType.MESH_LEFT : HandPoseType.MESH_RIGHT);
|
||||||
|
if (handPose != null && handPose is MeshHandPose meshHandPose)
|
||||||
|
{
|
||||||
|
meshHandPose.GetPosition(JointType.Wrist, out Vector3 wristPosition);
|
||||||
|
meshHandPose.GetRotation(JointType.Wrist, out Quaternion wristRotation);
|
||||||
|
|
||||||
|
Quaternion[] fingerJointRotation = new Quaternion[(int)JointType.Count];
|
||||||
|
for (int i = 0; i < fingerJointRotation.Length; i++)
|
||||||
|
{
|
||||||
|
meshHandPose.GetRotation((JointType)i, out Quaternion jointRotation, local: true);
|
||||||
|
fingerJointRotation[i] = jointRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrabPose grabPose = handGrabbable.grabPoses[m_PreviewIndex.intValue];
|
||||||
|
grabPose.Update(grabPoseName.stringValue, fingerJointRotation, isLeft.boolValue);
|
||||||
|
grabPose.grabOffset.Update(wristPosition, wristRotation, handGrabbable.transform.position, handGrabbable.transform.rotation);
|
||||||
|
handGrabbable.grabPoses[m_PreviewIndex.intValue] = grabPose;
|
||||||
|
GrabbablePoseRecorder.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7ba007edb5befc349a95aa1cfa7cb6c9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
namespace VIVE.OpenXR.Toolkits.RealisticHandInteraction
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(HandGrabInteractor))]
|
||||||
|
public class HandGrabInteractorEditor : UnityEditor.Editor
|
||||||
|
{
|
||||||
|
private SerializedProperty m_Handedness, m_GrabDistance, m_OnBeginGrab, m_OnEndGrab;
|
||||||
|
private bool showEvent = false;
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
m_Handedness = serializedObject.FindProperty("m_Handedness");
|
||||||
|
m_GrabDistance = serializedObject.FindProperty("m_GrabDistance");
|
||||||
|
m_OnBeginGrab = serializedObject.FindProperty("m_OnBeginGrab");
|
||||||
|
m_OnEndGrab = serializedObject.FindProperty("m_OnEndGrab");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
serializedObject.Update();
|
||||||
|
EditorGUILayout.PropertyField(m_Handedness);
|
||||||
|
EditorGUILayout.PropertyField(m_GrabDistance);
|
||||||
|
showEvent = EditorGUILayout.Foldout(showEvent, "Grab Event");
|
||||||
|
if (showEvent)
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(m_OnBeginGrab);
|
||||||
|
EditorGUILayout.PropertyField(m_OnEndGrab);
|
||||||
|
|
||||||
|
}
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a8e7c213fed939e4b859638cde97a31c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
namespace VIVE.OpenXR.Toolkits.RealisticHandInteraction
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(HandMeshManager))]
|
||||||
|
public class HandMeshManagerEditor : Editor
|
||||||
|
{
|
||||||
|
private HandMeshManager m_HandMesh;
|
||||||
|
private SerializedProperty m_Handedness, m_EnableCollider, m_HandJoints;
|
||||||
|
|
||||||
|
private bool showJoints = false;
|
||||||
|
public static readonly GUIContent findJoints = EditorGUIUtility.TrTextContent("Find Joints");
|
||||||
|
public static readonly GUIContent clearJoints = EditorGUIUtility.TrTextContent("All Clear");
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
m_HandMesh = target as HandMeshManager;
|
||||||
|
m_Handedness = serializedObject.FindProperty("m_Handedness");
|
||||||
|
m_EnableCollider = serializedObject.FindProperty("m_EnableCollider");
|
||||||
|
m_HandJoints = serializedObject.FindProperty("m_HandJoints");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
serializedObject.Update();
|
||||||
|
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
EditorGUILayout.HelpBox("Please check if your model is used to bind left hand poses", MessageType.None);
|
||||||
|
EditorGUILayout.PropertyField(m_Handedness, new GUIContent("Handedness"));
|
||||||
|
|
||||||
|
EditorGUILayout.HelpBox("Please check if you want the hand model with collision enabled.", MessageType.None);
|
||||||
|
EditorGUILayout.PropertyField(m_EnableCollider, new GUIContent("Enable Collider"));
|
||||||
|
|
||||||
|
showJoints = EditorGUILayout.Foldout(showJoints, "Hand Bones Reference");
|
||||||
|
if (showJoints)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("Please change rotation to make sure your model should palm faces forward and fingers points up in global axis.", MessageType.Info);
|
||||||
|
|
||||||
|
using (new EditorGUILayout.HorizontalScope())
|
||||||
|
{
|
||||||
|
using (new EditorGUI.DisabledScope())
|
||||||
|
{
|
||||||
|
if (GUILayout.Button(findJoints))
|
||||||
|
{
|
||||||
|
m_HandMesh.FindJoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (new EditorGUI.DisabledScope())
|
||||||
|
{
|
||||||
|
if (GUILayout.Button(clearJoints))
|
||||||
|
{
|
||||||
|
m_HandMesh.ClearJoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDetected = false;
|
||||||
|
for (int i = 0; i < m_HandJoints.arraySize; i++)
|
||||||
|
{
|
||||||
|
SerializedProperty bone = m_HandJoints.GetArrayElementAtIndex(i);
|
||||||
|
Transform boneTransform = (Transform)bone.objectReferenceValue;
|
||||||
|
if (boneTransform != null)
|
||||||
|
{
|
||||||
|
isDetected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDetected)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_HandJoints.arraySize; i++)
|
||||||
|
{
|
||||||
|
SerializedProperty bone = m_HandJoints.GetArrayElementAtIndex(i);
|
||||||
|
EditorGUILayout.PropertyField(bone, new GUIContent(((JointType)i).ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d606d68d81c647241b90f1060786476c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e312e5246287a2546944bb77519270c4
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3744e08fd384e154c82d53686f0c82a6
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 44fadaef2720de846af62a7bbb2ec370
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!91 &9100000
|
||||||
|
AnimatorController:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: LeftHandGrab
|
||||||
|
serializedVersion: 5
|
||||||
|
m_AnimatorParameters: []
|
||||||
|
m_AnimatorLayers:
|
||||||
|
- serializedVersion: 5
|
||||||
|
m_Name: Base Layer
|
||||||
|
m_StateMachine: {fileID: 9113736214857964471}
|
||||||
|
m_Mask: {fileID: 0}
|
||||||
|
m_Motions: []
|
||||||
|
m_Behaviours: []
|
||||||
|
m_BlendingMode: 0
|
||||||
|
m_SyncedLayerIndex: -1
|
||||||
|
m_DefaultWeight: 0
|
||||||
|
m_IKPass: 0
|
||||||
|
m_SyncedLayerAffectsTiming: 0
|
||||||
|
m_Controller: {fileID: 9100000}
|
||||||
|
--- !u!1102 &83218441301297147
|
||||||
|
AnimatorState:
|
||||||
|
serializedVersion: 6
|
||||||
|
m_ObjectHideFlags: 1
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: LeftHandGrab
|
||||||
|
m_Speed: 1
|
||||||
|
m_CycleOffset: 0
|
||||||
|
m_Transitions: []
|
||||||
|
m_StateMachineBehaviours: []
|
||||||
|
m_Position: {x: 50, y: 50, z: 0}
|
||||||
|
m_IKOnFeet: 0
|
||||||
|
m_WriteDefaultValues: 1
|
||||||
|
m_Mirror: 0
|
||||||
|
m_SpeedParameterActive: 0
|
||||||
|
m_MirrorParameterActive: 0
|
||||||
|
m_CycleOffsetParameterActive: 0
|
||||||
|
m_TimeParameterActive: 0
|
||||||
|
m_Motion: {fileID: 7400000, guid: 44fadaef2720de846af62a7bbb2ec370, type: 2}
|
||||||
|
m_Tag:
|
||||||
|
m_SpeedParameter:
|
||||||
|
m_MirrorParameter:
|
||||||
|
m_CycleOffsetParameter:
|
||||||
|
m_TimeParameter:
|
||||||
|
--- !u!1107 &9113736214857964471
|
||||||
|
AnimatorStateMachine:
|
||||||
|
serializedVersion: 6
|
||||||
|
m_ObjectHideFlags: 1
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: Base Layer
|
||||||
|
m_ChildStates:
|
||||||
|
- serializedVersion: 1
|
||||||
|
m_State: {fileID: 83218441301297147}
|
||||||
|
m_Position: {x: 200, y: 0, z: 0}
|
||||||
|
m_ChildStateMachines: []
|
||||||
|
m_AnyStateTransitions: []
|
||||||
|
m_EntryTransitions: []
|
||||||
|
m_StateMachineTransitions: {}
|
||||||
|
m_StateMachineBehaviours: []
|
||||||
|
m_AnyStatePosition: {x: 50, y: 20, z: 0}
|
||||||
|
m_EntryPosition: {x: 50, y: 120, z: 0}
|
||||||
|
m_ExitPosition: {x: 800, y: 120, z: 0}
|
||||||
|
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
|
||||||
|
m_DefaultState: {fileID: 83218441301297147}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1504c5149524a8a44a3854dfd4f94d1e
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 9100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a10ddbfcbce64f84787e026f4fc003a4
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1107 &-3394168293143139300
|
||||||
|
AnimatorStateMachine:
|
||||||
|
serializedVersion: 6
|
||||||
|
m_ObjectHideFlags: 1
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: Base Layer
|
||||||
|
m_ChildStates:
|
||||||
|
- serializedVersion: 1
|
||||||
|
m_State: {fileID: 3187830036533893518}
|
||||||
|
m_Position: {x: 200, y: 0, z: 0}
|
||||||
|
m_ChildStateMachines: []
|
||||||
|
m_AnyStateTransitions: []
|
||||||
|
m_EntryTransitions: []
|
||||||
|
m_StateMachineTransitions: {}
|
||||||
|
m_StateMachineBehaviours: []
|
||||||
|
m_AnyStatePosition: {x: 50, y: 20, z: 0}
|
||||||
|
m_EntryPosition: {x: 50, y: 120, z: 0}
|
||||||
|
m_ExitPosition: {x: 800, y: 120, z: 0}
|
||||||
|
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
|
||||||
|
m_DefaultState: {fileID: 3187830036533893518}
|
||||||
|
--- !u!91 &9100000
|
||||||
|
AnimatorController:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: RightHandGrab
|
||||||
|
serializedVersion: 5
|
||||||
|
m_AnimatorParameters: []
|
||||||
|
m_AnimatorLayers:
|
||||||
|
- serializedVersion: 5
|
||||||
|
m_Name: Base Layer
|
||||||
|
m_StateMachine: {fileID: -3394168293143139300}
|
||||||
|
m_Mask: {fileID: 0}
|
||||||
|
m_Motions: []
|
||||||
|
m_Behaviours: []
|
||||||
|
m_BlendingMode: 0
|
||||||
|
m_SyncedLayerIndex: -1
|
||||||
|
m_DefaultWeight: 0
|
||||||
|
m_IKPass: 0
|
||||||
|
m_SyncedLayerAffectsTiming: 0
|
||||||
|
m_Controller: {fileID: 9100000}
|
||||||
|
--- !u!1102 &3187830036533893518
|
||||||
|
AnimatorState:
|
||||||
|
serializedVersion: 6
|
||||||
|
m_ObjectHideFlags: 1
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: RightHandGrab
|
||||||
|
m_Speed: 1
|
||||||
|
m_CycleOffset: 0
|
||||||
|
m_Transitions: []
|
||||||
|
m_StateMachineBehaviours: []
|
||||||
|
m_Position: {x: 50, y: 50, z: 0}
|
||||||
|
m_IKOnFeet: 0
|
||||||
|
m_WriteDefaultValues: 1
|
||||||
|
m_Mirror: 0
|
||||||
|
m_SpeedParameterActive: 0
|
||||||
|
m_MirrorParameterActive: 0
|
||||||
|
m_CycleOffsetParameterActive: 0
|
||||||
|
m_TimeParameterActive: 0
|
||||||
|
m_Motion: {fileID: 7400000, guid: a10ddbfcbce64f84787e026f4fc003a4, type: 2}
|
||||||
|
m_Tag:
|
||||||
|
m_SpeedParameter:
|
||||||
|
m_MirrorParameter:
|
||||||
|
m_CycleOffsetParameter:
|
||||||
|
m_TimeParameter:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 950573398b7eb5149bea536bfa6107ca
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 9100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: cb1dc53a6d599b74c87e1c691fe77d21
|
guid: 32007394341a17c44859030ef5b809ee
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: a875c1780d640dd41a6eac1b6b8f27bb
|
guid: 010c0098d232cb0428f42a48488a6255
|
||||||
PrefabImporter:
|
PrefabImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: d521c5c5e79afd943a590248701d29b2
|
guid: 9731229184277b54ba66bffd1633169b
|
||||||
PrefabImporter:
|
PrefabImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9b7f1f69b8b23e9459efda715941fbee
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ea1b2b1a3faba024aa3df5391529aec0
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// "Wave SDK
|
||||||
|
// © 2020 HTC Corporation. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Unless otherwise required by copyright law and practice,
|
||||||
|
// upon the execution of HTC SDK license agreement,
|
||||||
|
// HTC grants you access to and use of the Wave SDK(s).
|
||||||
|
// You shall fully comply with all of HTC’s SDK license agreement terms and
|
||||||
|
// conditions signed by you and all SDK and API requirements,
|
||||||
|
// specifications, and documentation provided by HTC to You."
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.Toolkits.RealisticHandInteraction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class is designed to automatically generate indicators.
|
||||||
|
/// </summary>
|
||||||
|
public class AutoGenIndicator : MonoBehaviour
|
||||||
|
{
|
||||||
|
private MeshRenderer meshRenderer;
|
||||||
|
private MeshFilter meshFilter;
|
||||||
|
|
||||||
|
private readonly Color indicatorColor = new Color(1f, 0.7960785f, 0.09411766f, 1f);
|
||||||
|
private const float k_Length = 0.05f;
|
||||||
|
private const float k_Width = 0.05f;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
meshRenderer = transform.gameObject.AddComponent<MeshRenderer>();
|
||||||
|
meshFilter = transform.gameObject.AddComponent<MeshFilter>();
|
||||||
|
MeshInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the mesh for the indicator.
|
||||||
|
/// </summary>
|
||||||
|
private void MeshInitialize()
|
||||||
|
{
|
||||||
|
Shader shader = Shader.Find("Sprites/Default");
|
||||||
|
meshRenderer.material = new Material(shader);
|
||||||
|
meshRenderer.material.SetColor("_Color", indicatorColor);
|
||||||
|
meshRenderer.sortingOrder = 1;
|
||||||
|
|
||||||
|
Mesh arrowMesh = new Mesh();
|
||||||
|
Vector3[] vertices = new Vector3[4];
|
||||||
|
int[] triangles = new int[3 * 2];
|
||||||
|
|
||||||
|
vertices[0] = new Vector3(0, 0f, 0f);
|
||||||
|
vertices[1] = new Vector3(0f, k_Length * 0.8f, 0f);
|
||||||
|
vertices[2] = new Vector3(-k_Width * 0.5f, k_Length, 0f);
|
||||||
|
vertices[3] = new Vector3(k_Width * 0.5f, k_Length, 0f);
|
||||||
|
|
||||||
|
triangles[0] = 0;
|
||||||
|
triangles[1] = 2;
|
||||||
|
triangles[2] = 1;
|
||||||
|
|
||||||
|
triangles[3] = 0;
|
||||||
|
triangles[4] = 1;
|
||||||
|
triangles[5] = 3;
|
||||||
|
|
||||||
|
arrowMesh.vertices = vertices;
|
||||||
|
arrowMesh.triangles = triangles;
|
||||||
|
|
||||||
|
arrowMesh.RecalculateNormals();
|
||||||
|
|
||||||
|
meshFilter.mesh = arrowMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the pose of the indicator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="position">The position vector to set.</param>
|
||||||
|
/// <param name="direction">The direction vector to set.</param>
|
||||||
|
public void SetPose(Vector3 position, Vector3 direction)
|
||||||
|
{
|
||||||
|
transform.position = position;
|
||||||
|
transform.up = direction.normalized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4694bdada589dce4c9b7096c7169833f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,378 @@
|
|||||||
|
// "Wave SDK
|
||||||
|
// © 2020 HTC Corporation. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Unless otherwise required by copyright law and practice,
|
||||||
|
// upon the execution of HTC SDK license agreement,
|
||||||
|
// HTC grants you access to and use of the Wave SDK(s).
|
||||||
|
// You shall fully comply with all of HTC’s SDK license agreement terms and
|
||||||
|
// conditions signed by you and all SDK and API requirements,
|
||||||
|
// specifications, and documentation provided by HTC to You."
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.Toolkits.RealisticHandInteraction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class is designed to edit grab gestures.
|
||||||
|
/// </summary>
|
||||||
|
[RequireComponent(typeof(HandMeshManager))]
|
||||||
|
public class CustomGrabPose : MonoBehaviour
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
|
||||||
|
#region Log
|
||||||
|
|
||||||
|
const string LOG_TAG = "Wave.Essence.Hand.Interaction.CustomGrabPose";
|
||||||
|
private StringBuilder m_sb = null;
|
||||||
|
internal StringBuilder sb
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_sb == null) { m_sb = new StringBuilder(); }
|
||||||
|
return m_sb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void DEBUG(StringBuilder msg) { Debug.Log($"{LOG_TAG}, {msg}"); }
|
||||||
|
private void WARNING(StringBuilder msg) { Debug.LogWarning($"{LOG_TAG}, {msg}"); }
|
||||||
|
private void ERROR(StringBuilder msg) { Debug.LogError($"{LOG_TAG}, {msg}"); }
|
||||||
|
int logFrame = 0;
|
||||||
|
bool printIntervalLog => logFrame == 0;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This structure is designed to record the rotation values of each joint at different bend angles.
|
||||||
|
/// </summary>
|
||||||
|
private struct JointBendingRotation
|
||||||
|
{
|
||||||
|
public FingerId fingerId;
|
||||||
|
public string jointPath;
|
||||||
|
public int bending;
|
||||||
|
public Quaternion rotation;
|
||||||
|
|
||||||
|
public JointBendingRotation(FingerId in_FingerId, string in_JointPath, int in_Bending, Quaternion in_Rotation)
|
||||||
|
{
|
||||||
|
fingerId = in_FingerId;
|
||||||
|
jointPath = in_JointPath;
|
||||||
|
bending = in_Bending;
|
||||||
|
rotation = in_Rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JointBendingRotation Identity => new JointBendingRotation(FingerId.Invalid, "", -1, Quaternion.identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
private AnimationClip animationClip;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[Range(1, 30)]
|
||||||
|
private int thumbBending = 1;
|
||||||
|
[SerializeField]
|
||||||
|
[Range(1, 30)]
|
||||||
|
private int indexBending = 1;
|
||||||
|
[SerializeField]
|
||||||
|
[Range(1, 30)]
|
||||||
|
private int middleBending = 1;
|
||||||
|
[SerializeField]
|
||||||
|
[Range(1, 30)]
|
||||||
|
private int ringBending = 1;
|
||||||
|
[SerializeField]
|
||||||
|
[Range(1, 30)]
|
||||||
|
private int pinkyBending = 1;
|
||||||
|
|
||||||
|
private HandMeshManager m_HandMesh;
|
||||||
|
private Dictionary<FingerId, int> fingersBendingMapping = new Dictionary<FingerId, int>()
|
||||||
|
{
|
||||||
|
{FingerId.Thumb, 1 },
|
||||||
|
{FingerId.Index, 1 },
|
||||||
|
{FingerId.Middle, 1 },
|
||||||
|
{FingerId.Ring, 1 },
|
||||||
|
{FingerId.Pinky, 1 },
|
||||||
|
};
|
||||||
|
private static readonly Dictionary<string, FingerId> jointsPathMapping = new Dictionary<string, FingerId>()
|
||||||
|
{
|
||||||
|
{"WaveBone_0", FingerId.Invalid },
|
||||||
|
{"WaveBone_1", FingerId.Invalid },
|
||||||
|
{"WaveBone_2", FingerId.Thumb },
|
||||||
|
{"WaveBone_2/WaveBone_3", FingerId.Thumb },
|
||||||
|
{"WaveBone_2/WaveBone_3/WaveBone_4", FingerId.Thumb },
|
||||||
|
{"WaveBone_2/WaveBone_3/WaveBone_4/WaveBone_5", FingerId.Thumb },
|
||||||
|
{"WaveBone_6", FingerId.Index },
|
||||||
|
{"WaveBone_6/WaveBone_7", FingerId.Index },
|
||||||
|
{"WaveBone_6/WaveBone_7/WaveBone_8", FingerId.Index },
|
||||||
|
{"WaveBone_6/WaveBone_7/WaveBone_8/WaveBone_9", FingerId.Index },
|
||||||
|
{"WaveBone_6/WaveBone_7/WaveBone_8/WaveBone_9/WaveBone_10", FingerId.Index },
|
||||||
|
{"WaveBone_11", FingerId.Middle },
|
||||||
|
{"WaveBone_11/WaveBone_12", FingerId.Middle },
|
||||||
|
{"WaveBone_11/WaveBone_12/WaveBone_13", FingerId.Middle },
|
||||||
|
{"WaveBone_11/WaveBone_12/WaveBone_13/WaveBone_14", FingerId.Middle },
|
||||||
|
{"WaveBone_11/WaveBone_12/WaveBone_13/WaveBone_14/WaveBone_15", FingerId.Middle },
|
||||||
|
{"WaveBone_16", FingerId.Ring },
|
||||||
|
{"WaveBone_16/WaveBone_17", FingerId.Ring },
|
||||||
|
{"WaveBone_16/WaveBone_17/WaveBone_18", FingerId.Ring },
|
||||||
|
{"WaveBone_16/WaveBone_17/WaveBone_18/WaveBone_19", FingerId.Ring },
|
||||||
|
{"WaveBone_16/WaveBone_17/WaveBone_18/WaveBone_19/WaveBone_20", FingerId.Ring },
|
||||||
|
{"WaveBone_21", FingerId.Pinky },
|
||||||
|
{"WaveBone_21/WaveBone_22", FingerId.Pinky },
|
||||||
|
{"WaveBone_21/WaveBone_22/WaveBone_23", FingerId.Pinky },
|
||||||
|
{"WaveBone_21/WaveBone_22/WaveBone_23/WaveBone_24", FingerId.Pinky },
|
||||||
|
{"WaveBone_21/WaveBone_22/WaveBone_23/WaveBone_24/WaveBone_25", FingerId.Pinky },
|
||||||
|
};
|
||||||
|
private List<JointBendingRotation> jointsBending = new List<JointBendingRotation>();
|
||||||
|
|
||||||
|
private readonly float k_GrabDistance = 0.1f;
|
||||||
|
private HandGrabInteractable candidate = null;
|
||||||
|
private Pose wristPose = Pose.identity;
|
||||||
|
private Quaternion[] fingerJointRotation = new Quaternion[jointsPathMapping.Count];
|
||||||
|
|
||||||
|
#region MonoBehaviours
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
m_HandMesh = transform.GetComponent<HandMeshManager>();
|
||||||
|
if (m_HandMesh == null)
|
||||||
|
{
|
||||||
|
sb.Clear().Append("Failed to find HandMeshRenderer.");
|
||||||
|
ERROR(sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animationClip != null)
|
||||||
|
{
|
||||||
|
EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(animationClip);
|
||||||
|
|
||||||
|
foreach (string propertyName in jointsPathMapping.Keys)
|
||||||
|
{
|
||||||
|
SetEachFrameRotation(curveBindings, propertyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Clear().Append("Failed to find hand grab animation. The hand model will not change when you change bend angles of finger.");
|
||||||
|
sb.Append("However, you still can record grab pose if you use direct preview mode.");
|
||||||
|
WARNING(sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
jointsBending.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (IsFingerBendingUpdated())
|
||||||
|
{
|
||||||
|
for (int i = 0; i < fingerJointRotation.Length; i++)
|
||||||
|
{
|
||||||
|
var jointInfo = jointsPathMapping.ElementAt(i);
|
||||||
|
string jointPath = jointInfo.Key;
|
||||||
|
FingerId fingerId = jointInfo.Value;
|
||||||
|
int bending = -1;
|
||||||
|
if (fingersBendingMapping.ContainsKey(fingerId))
|
||||||
|
{
|
||||||
|
bending = fingersBendingMapping[fingerId] - 1;
|
||||||
|
}
|
||||||
|
if (jointsBending.Count(x => x.fingerId == fingerId && x.jointPath == jointPath && x.bending == bending) > 0)
|
||||||
|
{
|
||||||
|
JointBendingRotation jointRotation = jointsBending.FirstOrDefault(x => x.fingerId == fingerId && x.jointPath == jointPath && x.bending == bending);
|
||||||
|
fingerJointRotation[i] = jointRotation.rotation;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fingerJointRotation[i] = Quaternion.identity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_HandMesh != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < fingerJointRotation.Length; i++)
|
||||||
|
{
|
||||||
|
JointType joint = (JointType)i;
|
||||||
|
m_HandMesh.GetJointPositionAndRotation(joint, out Vector3 jointPosition, out _, local: true);
|
||||||
|
m_HandMesh.SetJointPositionAndRotation(joint, jointPosition, fingerJointRotation[i], local: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter))
|
||||||
|
{
|
||||||
|
FindNearInteractable();
|
||||||
|
SavePoseWithCandidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the rotation of each joint frame by frame and records it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="curveBindings">All the float curve bindings currently stored in the clip.</param>
|
||||||
|
/// <param name="jointPath">The path of the joint.</param>
|
||||||
|
private void SetEachFrameRotation(EditorCurveBinding[] curveBindings, string jointPath)
|
||||||
|
{
|
||||||
|
const int propertyCount = 4;
|
||||||
|
const int animeCount = 30;
|
||||||
|
const float animeFPS = 60.0f;
|
||||||
|
|
||||||
|
List<EditorCurveBinding> matchCurve = new List<EditorCurveBinding>();
|
||||||
|
foreach (EditorCurveBinding binding in curveBindings)
|
||||||
|
{
|
||||||
|
if (binding.path.Equals(jointPath))
|
||||||
|
{
|
||||||
|
matchCurve.Add(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchCurve.Count == propertyCount)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchCurve.Count == propertyCount)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < animeCount; i++)
|
||||||
|
{
|
||||||
|
Quaternion rotation = Quaternion.identity;
|
||||||
|
foreach (var curveBinding in matchCurve)
|
||||||
|
{
|
||||||
|
AnimationCurve curve = AnimationUtility.GetEditorCurve(animationClip, curveBinding);
|
||||||
|
switch (curveBinding.propertyName)
|
||||||
|
{
|
||||||
|
case "m_LocalRotation.x":
|
||||||
|
rotation.x = curve.Evaluate(i / animeFPS);
|
||||||
|
break;
|
||||||
|
case "m_LocalRotation.y":
|
||||||
|
rotation.y = curve.Evaluate(i / animeFPS);
|
||||||
|
break;
|
||||||
|
case "m_LocalRotation.z":
|
||||||
|
rotation.z = curve.Evaluate(i / animeFPS);
|
||||||
|
break;
|
||||||
|
case "m_LocalRotation.w":
|
||||||
|
rotation.w = curve.Evaluate(i / animeFPS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jointsBending.Add(new JointBendingRotation(jointsPathMapping[jointPath], jointPath, i, rotation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the current finger bend angle has changed.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the bend angle has changed; otherwise, false.</returns>
|
||||||
|
private bool IsFingerBendingUpdated()
|
||||||
|
{
|
||||||
|
bool updated = false;
|
||||||
|
|
||||||
|
if (fingersBendingMapping[FingerId.Thumb] != thumbBending)
|
||||||
|
{
|
||||||
|
fingersBendingMapping[FingerId.Thumb] = thumbBending;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
if (fingersBendingMapping[FingerId.Index] != indexBending)
|
||||||
|
{
|
||||||
|
fingersBendingMapping[FingerId.Index] = indexBending;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
if (fingersBendingMapping[FingerId.Middle] != middleBending)
|
||||||
|
{
|
||||||
|
fingersBendingMapping[FingerId.Middle] = middleBending;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
if (fingersBendingMapping[FingerId.Ring] != ringBending)
|
||||||
|
{
|
||||||
|
fingersBendingMapping[FingerId.Ring] = ringBending;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
if (fingersBendingMapping[FingerId.Pinky] != pinkyBending)
|
||||||
|
{
|
||||||
|
fingersBendingMapping[FingerId.Pinky] = pinkyBending;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update hand pose from HandMeshRenderer.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Return true if updating hand pose from HandMeshRenderer; otherwise.</returns>
|
||||||
|
private bool UpdateHandPose()
|
||||||
|
{
|
||||||
|
bool updated = false;
|
||||||
|
if (m_HandMesh != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < fingerJointRotation.Length; i++)
|
||||||
|
{
|
||||||
|
if (i == (int)JointType.Wrist)
|
||||||
|
{
|
||||||
|
m_HandMesh.GetJointPositionAndRotation(JointType.Wrist, out wristPose.position, out wristPose.rotation);
|
||||||
|
}
|
||||||
|
m_HandMesh.GetJointPositionAndRotation((JointType)i, out _, out fingerJointRotation[i], local: true);
|
||||||
|
}
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
if (!updated)
|
||||||
|
{
|
||||||
|
sb.Clear().Append("Failed to update hand pose.");
|
||||||
|
DEBUG(sb);
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the nearest interactable object to the hand.
|
||||||
|
/// </summary>
|
||||||
|
public void FindNearInteractable()
|
||||||
|
{
|
||||||
|
if (!UpdateHandPose()) { return; }
|
||||||
|
|
||||||
|
candidate = null;
|
||||||
|
float maxScore = 0;
|
||||||
|
foreach (HandGrabInteractable interactable in GrabManager.handGrabbables)
|
||||||
|
{
|
||||||
|
float distanceScore = interactable.CalculateDistanceScore(wristPose.position, k_GrabDistance);
|
||||||
|
if (distanceScore > maxScore)
|
||||||
|
{
|
||||||
|
maxScore = distanceScore;
|
||||||
|
candidate = interactable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (candidate == null)
|
||||||
|
{
|
||||||
|
sb.Clear().Append("Unable to find a suitable candidate.");
|
||||||
|
WARNING(sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save the position and rotation offset with the candidate.
|
||||||
|
/// </summary>
|
||||||
|
public void SavePoseWithCandidate()
|
||||||
|
{
|
||||||
|
if (!UpdateHandPose() || candidate == null) { return; }
|
||||||
|
|
||||||
|
Quaternion[] clone = new Quaternion[fingerJointRotation.Length];
|
||||||
|
Array.Copy(fingerJointRotation, clone, fingerJointRotation.Length);
|
||||||
|
GrabPose grabPose = GrabPose.Identity;
|
||||||
|
|
||||||
|
grabPose.Update($"Grab Pose {candidate.grabPoses.Count + 1}", clone, m_HandMesh.isLeft);
|
||||||
|
grabPose.grabOffset = new GrabOffset(wristPose.position, wristPose.rotation, candidate.transform.position, candidate.transform.rotation);
|
||||||
|
if (!candidate.grabPoses.Contains(grabPose))
|
||||||
|
{
|
||||||
|
candidate.grabPoses.Add(grabPose);
|
||||||
|
}
|
||||||
|
GrabbablePoseRecorder.SaveChanges();
|
||||||
|
|
||||||
|
sb.Clear().Append("Save grab pose successfully.");
|
||||||
|
DEBUG(sb);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fd5957dc7b39bd249885b5bb53749b7a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.Toolkits.RealisticHandInteraction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class is designed to manage all Grabbers and Grabbables.
|
||||||
|
/// </summary>
|
||||||
|
public static class GrabManager
|
||||||
|
{
|
||||||
|
private static List<IGrabber> m_GrabberRegistry = new List<IGrabber>();
|
||||||
|
public static IReadOnlyList<HandGrabInteractor> handGrabbers => m_GrabberRegistry.OfType<HandGrabInteractor>().ToList().AsReadOnly();
|
||||||
|
|
||||||
|
private static List<IGrabbable> m_GrabbableRegistry = new List<IGrabbable>();
|
||||||
|
public static IReadOnlyList<HandGrabInteractable> handGrabbables => m_GrabbableRegistry.OfType<HandGrabInteractable>().ToList().AsReadOnly();
|
||||||
|
|
||||||
|
#region IGrabber
|
||||||
|
/// <summary>
|
||||||
|
/// Register the grabber in the grabber registry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="grabber">The grabber to register.</param>
|
||||||
|
/// <returns>True if the grabber is successfully registered; otherwise, false.</returns>
|
||||||
|
public static bool RegisterGrabber(IGrabber grabber)
|
||||||
|
{
|
||||||
|
if (!m_GrabberRegistry.Contains(grabber))
|
||||||
|
{
|
||||||
|
m_GrabberRegistry.Add(grabber);
|
||||||
|
}
|
||||||
|
return m_GrabberRegistry.Contains(grabber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the grabber from the grabber registry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="grabber">The grabber to remove.</param>
|
||||||
|
/// <returns>True if the grabber is successfully removed; otherwise, false.</returns>
|
||||||
|
public static bool UnregisterGrabber(IGrabber grabber)
|
||||||
|
{
|
||||||
|
if (m_GrabberRegistry.Contains(grabber))
|
||||||
|
{
|
||||||
|
m_GrabberRegistry.Remove(grabber);
|
||||||
|
}
|
||||||
|
return !m_GrabberRegistry.Contains(grabber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the first hand grabber component found in the child hierarchy of the GameObject.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The target whose child hierarchy to search.</param>
|
||||||
|
/// <param name="grabber">The output parameter to store the first hand grabber component found.</param>
|
||||||
|
/// <returns>True if a hand grabber component is found; otherwise, false.</returns>
|
||||||
|
public static bool GetFirstHandGrabberFromChild(GameObject target, out HandGrabInteractor grabber)
|
||||||
|
{
|
||||||
|
grabber = TopDownFind<HandGrabInteractor>(target.transform);
|
||||||
|
return grabber != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the first hand grabber component found in the parent hierarchy of the GameObject.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The target whose parent hierarchy to search.</param>
|
||||||
|
/// <param name="grabber">The output parameter to store the first hand grabber component found.</param>
|
||||||
|
/// <returns>True if a hand grabber component is found; otherwise, false.</returns>
|
||||||
|
public static bool GetFirstHandGrabberFromParent(GameObject target, out HandGrabInteractor grabber)
|
||||||
|
{
|
||||||
|
grabber = BottomUpFind<HandGrabInteractor>(target.transform);
|
||||||
|
return grabber != null;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GrabInteractable
|
||||||
|
/// <summary>
|
||||||
|
/// Register the grabbable in the grabbable registry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="grabbable">The grabbable to register.</param>
|
||||||
|
/// <returns>True if the grabbable is successfully registered; otherwise, false.</returns>
|
||||||
|
public static bool RegisterGrabbable(IGrabbable grabbable)
|
||||||
|
{
|
||||||
|
if (!m_GrabbableRegistry.Contains(grabbable))
|
||||||
|
{
|
||||||
|
m_GrabbableRegistry.Add(grabbable);
|
||||||
|
}
|
||||||
|
return m_GrabbableRegistry.Contains(grabbable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the grabbable from the grabbable registry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="grabbable">The grabbable to remove.</param>
|
||||||
|
/// <returns>True if the grabbable is successfully removed; otherwise, false.</returns>
|
||||||
|
public static bool UnregisterGrabbable(IGrabbable grabbable)
|
||||||
|
{
|
||||||
|
if (m_GrabbableRegistry.Contains(grabbable))
|
||||||
|
{
|
||||||
|
m_GrabbableRegistry.Remove(grabbable);
|
||||||
|
}
|
||||||
|
return !m_GrabbableRegistry.Contains(grabbable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the first hand grabbable component found in the child hierarchy of the GameObject.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The target whose child hierarchy to search.</param>
|
||||||
|
/// <param name="grabbable">The output parameter to store the first hand grabbable component found.</param>
|
||||||
|
/// <returns>True if a hand grabbable component is found; otherwise, false.</returns>
|
||||||
|
public static bool GetFirstHandGrabbableFromChild(GameObject target, out HandGrabInteractable grabbable)
|
||||||
|
{
|
||||||
|
grabbable = TopDownFind<HandGrabInteractable>(target.transform);
|
||||||
|
return grabbable != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the first hand grabbable component found in the parent hierarchy of the GameObject.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The target whose parent hierarchy to search.</param>
|
||||||
|
/// <param name="grabbable">The output parameter to store the first hand grabbable component found.</param>
|
||||||
|
/// <returns>True if a hand grabbable component is found; otherwise, false.</returns>
|
||||||
|
public static bool GetFirstHandGrabbableFromParent(GameObject target, out HandGrabInteractable grabbable)
|
||||||
|
{
|
||||||
|
grabbable = BottomUpFind<HandGrabInteractable>(target.transform);
|
||||||
|
return grabbable != null;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find available components from self to children nodes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="transform">The transform of the gameobject.</param>
|
||||||
|
/// <returns>Value for available component.</returns>
|
||||||
|
private static T TopDownFind<T>(Transform transform) where T : Component
|
||||||
|
{
|
||||||
|
T component = transform.GetComponent<T>();
|
||||||
|
if (component != null)
|
||||||
|
{
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transform.childCount > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < transform.childCount; i++)
|
||||||
|
{
|
||||||
|
T childComponent = TopDownFind<T>(transform.GetChild(i));
|
||||||
|
if (childComponent != null)
|
||||||
|
{
|
||||||
|
return childComponent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find available components from self to parent node.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="transform">The transform of the gameobject.</param>
|
||||||
|
/// <returns>Value for available component.</returns>
|
||||||
|
private static T BottomUpFind<T>(Transform transform) where T : Component
|
||||||
|
{
|
||||||
|
T component = transform.GetComponent<T>();
|
||||||
|
if (component != null)
|
||||||
|
{
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transform.parent != null)
|
||||||
|
{
|
||||||
|
return BottomUpFind<T>(transform.parent);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user