6 Commits

Author SHA1 Message Date
Sean Lu
2bfa2ad4c7 version 2.5.0 2024-12-06 15:44:37 +08:00
Sean Lu
dfdcd0fd7f version 2.4.2 2024-10-30 16:02:07 +08:00
babelsw_hsu
5ac252bf2e Changed: The License file.
Symptom: Plugin
Root Cause: N/A
Solution: Changed the License file.
Project: VIVE OpenXR Unity Plugin
Note:
2024-09-27 17:49:27 +08:00
Sean
fa1969a087 version 2.4.1 2024-07-18 14:29:33 +08:00
Sean Lu(呂祥榮)
3dd72f5f56 version 2.4.0 2024-07-03 14:58:53 +08:00
Sean Lu(呂祥榮)
7f2a459592 version 2.3.0 2024-05-15 14:09:18 +08:00
1313 changed files with 548671 additions and 84688 deletions

4
LICENSE.txt Normal file
View 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/

View File

@@ -1,6 +1,7 @@
// Copyright HTC Corporation All Rights Reserved. // Copyright HTC Corporation All Rights Reserved.
using System; using System;
using System.IO; using System.IO;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Xml; using System.Xml;
@@ -12,7 +13,6 @@ using UnityEngine;
using UnityEngine.XR.OpenXR; using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features; using UnityEngine.XR.OpenXR.Features;
using UnityEngine.XR.OpenXR.Features.Interactions; using UnityEngine.XR.OpenXR.Features.Interactions;
using VIVE.OpenXR.FacialTracking;
using VIVE.OpenXR.Hand; using VIVE.OpenXR.Hand;
using VIVE.OpenXR.Tracker; using VIVE.OpenXR.Tracker;
@@ -152,7 +152,7 @@ namespace VIVE.OpenXR.Editor
m_IsEnabled = enabled; m_IsEnabled = enabled;
sb.Clear().Append(LOG_TAG).Append(m_IsEnabled ? "Enable " : "Disable ").Append("Simultaneous Interaction."); DEBUG(sb); //sb.Clear().Append(LOG_TAG).Append(m_IsEnabled ? "Enable " : "Disable ").Append("Simultaneous Interaction."); DEBUG(sb);
} }
[MenuItem(MENU_NAME, validate = true, priority = 601)] [MenuItem(MENU_NAME, validate = true, priority = 601)]
@@ -255,6 +255,10 @@ namespace VIVE.OpenXR.Editor
bool enableTracker = false; bool enableTracker = false;
bool enableEyetracking = false; bool enableEyetracking = false;
bool enableLipexpression = false; bool enableLipexpression = false;
const string kHandTrackingExtension = "XR_EXT_hand_tracking";
const string kFacialTrackingExtension = "XR_HTC_facial_tracking";
const string kHandInteractionHTC = "XR_HTC_hand_interaction";
const string kHandInteractionEXT = "XR_EXT_hand_interaction";
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android); var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
if (null == settings) if (null == settings)
@@ -279,16 +283,34 @@ namespace VIVE.OpenXR.Editor
foreach (var feature in settings.GetFeatures<OpenXRFeature>()) foreach (var feature in settings.GetFeatures<OpenXRFeature>())
{ {
if (feature is ViveHandTracking && feature.enabled) if (!feature.enabled) { continue; }
FieldInfo fieldInfoOpenXrExtensionStrings = typeof(OpenXRFeature).GetField(
"openxrExtensionStrings",
BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfoOpenXrExtensionStrings != null)
{
var openXrExtensionStringsArray =
((string)fieldInfoOpenXrExtensionStrings.GetValue(feature)).Split(' ');
foreach (string stringItem in openXrExtensionStringsArray)
{
if (string.IsNullOrEmpty(stringItem)) { continue; }
if (stringItem.Equals(kHandTrackingExtension) ||
stringItem.Equals(kHandInteractionHTC) ||
stringItem.Equals(kHandInteractionEXT))
{ {
enableHandtracking = true; enableHandtracking = true;
} }
if (feature is ViveFacialTracking && feature.enabled) if (stringItem.Equals(kFacialTrackingExtension))
{ {
enableEyetracking = true; enableEyetracking = true;
enableLipexpression = true; enableLipexpression = true;
} }
} }
}
}
if (enableHandtracking) if (enableHandtracking)
{ {

View File

@@ -67,14 +67,40 @@ 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;
static string PropertyName_IsCustomRects = "isCustomRects";
static GUIContent Label_IsCustomRects = new GUIContent("Customize Rects", "Using a single texture as a stereo image");
SerializedProperty Property_IsCustomRects;
static string PropertyName_CustomRects = "customRects";
static GUIContent Label_CustomRects = new GUIContent("Customize Rects Type", "Specify the customize rects type of the left texture.");
SerializedProperty Property_CustomRects;
static string PropertyName_ApplyColorScaleBias = "applyColorScaleBias"; static string PropertyName_ApplyColorScaleBias = "applyColorScaleBias";
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,15 +122,19 @@ 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
Rect FullRect = new Rect(0, 0, 1, 1);
Rect LeftRightRect = new Rect(0, 0, 0.5f, 1);
Rect TopDownRect = new Rect(0, 0.5f, 1, 0.5f);
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);
if (Property_CompositionDepth == null) Property_CompositionDepth = serializedObject.FindProperty(PropertyName_CompositionDepth); if (Property_CompositionDepth == null) Property_CompositionDepth = serializedObject.FindProperty(PropertyName_CompositionDepth);
if (Property_LayerShape == null) Property_LayerShape = serializedObject.FindProperty(PropertyName_LayerShape); if (Property_LayerShape == null) Property_LayerShape = serializedObject.FindProperty(PropertyName_LayerShape);
if (Property_LayerVisibility == null) Property_LayerVisibility = serializedObject.FindProperty(PropertyName_LayerVisibility); if (Property_LayerVisibility == null) Property_LayerVisibility = serializedObject.FindProperty(PropertyName_LayerVisibility);
if (Property_CustomRects == null) Property_CustomRects = serializedObject.FindProperty(PropertyName_CustomRects);
if (Property_LockMode == null) Property_LockMode = serializedObject.FindProperty(PropertyName_LockMode); if (Property_LockMode == null) Property_LockMode = serializedObject.FindProperty(PropertyName_LockMode);
if (Property_QuadWidth == null) Property_QuadWidth = serializedObject.FindProperty(PropertyName_QuadWidth); if (Property_QuadWidth == null) Property_QuadWidth = serializedObject.FindProperty(PropertyName_QuadWidth);
if (Property_QuadHeight == null) Property_QuadHeight = serializedObject.FindProperty(PropertyName_QuadHeight); if (Property_QuadHeight == null) Property_QuadHeight = serializedObject.FindProperty(PropertyName_QuadHeight);
@@ -112,8 +142,13 @@ 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_IsCustomRects == null) Property_IsCustomRects = serializedObject.FindProperty(PropertyName_IsCustomRects);
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);
@@ -289,6 +324,12 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
{ {
if (GUILayout.Button("Show Cylinder Preview")) if (GUILayout.Button("Show Cylinder Preview"))
{ {
Rect srcRectLeft = FullRect;
if (targetCompositionLayer.isCustomRects && targetCompositionLayer.customRects == CompositionLayer.CustomRectsType.LeftRight)
srcRectLeft = LeftRightRect;
if (targetCompositionLayer.isCustomRects && targetCompositionLayer.customRects == CompositionLayer.CustomRectsType.TopDown)
srcRectLeft = TopDownRect;
targetCompositionLayer.isPreviewingCylinder = true; targetCompositionLayer.isPreviewingCylinder = true;
Vector3[] cylinderVertices = CompositionLayer.MeshGenerationHelper.GenerateCylinderVertex(targetCompositionLayer.CylinderAngleOfArc, targetCompositionLayer.CylinderRadius, targetCompositionLayer.CylinderHeight); Vector3[] cylinderVertices = CompositionLayer.MeshGenerationHelper.GenerateCylinderVertex(targetCompositionLayer.CylinderAngleOfArc, targetCompositionLayer.CylinderRadius, targetCompositionLayer.CylinderHeight);
//Add components to Game Object //Add components to Game Object
@@ -308,6 +349,8 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
if (targetCompositionLayer.texture != null) if (targetCompositionLayer.texture != null)
{ {
cylinderMeshRenderer.sharedMaterial.mainTexture = targetCompositionLayer.texture; cylinderMeshRenderer.sharedMaterial.mainTexture = targetCompositionLayer.texture;
cylinderMeshRenderer.sharedMaterial.mainTextureOffset = srcRectLeft.position;
cylinderMeshRenderer.sharedMaterial.mainTextureScale = srcRectLeft.size;
} }
//Generate Mesh //Generate Mesh
@@ -387,6 +430,12 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
{ {
if (GUILayout.Button("Show Quad Preview")) if (GUILayout.Button("Show Quad Preview"))
{ {
Rect srcRectLeft = FullRect;
if (targetCompositionLayer.isCustomRects && targetCompositionLayer.customRects == CompositionLayer.CustomRectsType.LeftRight)
srcRectLeft = LeftRightRect;
if (targetCompositionLayer.isCustomRects && targetCompositionLayer.customRects == CompositionLayer.CustomRectsType.TopDown)
srcRectLeft = TopDownRect;
targetCompositionLayer.isPreviewingQuad = true; targetCompositionLayer.isPreviewingQuad = true;
//Generate vertices //Generate vertices
Vector3[] quadVertices = CompositionLayer.MeshGenerationHelper.GenerateQuadVertex(targetCompositionLayer.quadWidth, targetCompositionLayer.quadHeight); Vector3[] quadVertices = CompositionLayer.MeshGenerationHelper.GenerateQuadVertex(targetCompositionLayer.quadWidth, targetCompositionLayer.quadHeight);
@@ -408,6 +457,8 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
if (targetCompositionLayer.texture != null) if (targetCompositionLayer.texture != null)
{ {
quadMeshRenderer.sharedMaterial.mainTexture = targetCompositionLayer.texture; quadMeshRenderer.sharedMaterial.mainTexture = targetCompositionLayer.texture;
quadMeshRenderer.sharedMaterial.mainTextureOffset = srcRectLeft.position;
quadMeshRenderer.sharedMaterial.mainTextureScale = srcRectLeft.size;
} }
//Generate Mesh //Generate Mesh
quadMeshFilter.mesh = CompositionLayer.MeshGenerationHelper.GenerateQuadMesh(quadVertices); quadMeshFilter.mesh = CompositionLayer.MeshGenerationHelper.GenerateQuadMesh(quadVertices);
@@ -418,11 +469,18 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
//Rect UI For textures //Rect UI For textures
Rect labelRect = EditorGUILayout.GetControlRect(); Rect labelRect = EditorGUILayout.GetControlRect();
EditorGUI.LabelField(new Rect(labelRect.x, labelRect.y, labelRect.width / 2, labelRect.height), new GUIContent("Texture", "Texture to be rendered on the layer")); EditorGUI.LabelField(new Rect(labelRect.x, labelRect.y, labelRect.width / 2, labelRect.height), new GUIContent("Left Texture", "Texture used for the left eye"));
EditorGUI.LabelField(new Rect(labelRect.x + labelRect.width / 2, labelRect.y, labelRect.width / 2, labelRect.height), new GUIContent("Right Texture", "Texture used for the right eye"));
Rect textureRect = EditorGUILayout.GetControlRect(GUILayout.Height(64)); Rect textureRect = EditorGUILayout.GetControlRect(GUILayout.Height(64));
targetCompositionLayer.texture = (Texture)EditorGUI.ObjectField(new Rect(textureRect.x, textureRect.y, 64, textureRect.height), targetCompositionLayer.texture, typeof(Texture), true); targetCompositionLayer.texture = (Texture)EditorGUI.ObjectField(new Rect(textureRect.x, textureRect.y, 64, textureRect.height), targetCompositionLayer.texture, typeof(Texture), true);
targetCompositionLayer.textureRight = (Texture)EditorGUI.ObjectField(new Rect(textureRect.x + textureRect.width / 2, textureRect.y, 64, textureRect.height), targetCompositionLayer.textureRight, typeof(Texture), true);
if (null == targetCompositionLayer.textureLeft)
{
targetCompositionLayer.texture = targetCompositionLayer.textureRight;
//myScript.textures[1] = right;
}
EditorGUILayout.PropertyField(Property_LayerVisibility, new GUIContent(Label_LayerVisibility)); EditorGUILayout.PropertyField(Property_LayerVisibility, new GUIContent(Label_LayerVisibility));
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
@@ -430,6 +488,38 @@ 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--;
}*/
if (targetCompositionLayer.textureLeft == targetCompositionLayer.textureRight || targetCompositionLayer.textureRight == null)
{
EditorGUILayout.PropertyField(Property_IsCustomRects, Label_IsCustomRects);
serializedObject.ApplyModifiedProperties();
}
if (targetCompositionLayer.isCustomRects)
{
EditorGUILayout.PropertyField(Property_CustomRects, new GUIContent(Label_CustomRects));
serializedObject.ApplyModifiedProperties();
}
EditorGUILayout.Space();
EditorGUILayout.PropertyField(Property_ApplyColorScaleBias, Label_ApplyColorScaleBias); EditorGUILayout.PropertyField(Property_ApplyColorScaleBias, Label_ApplyColorScaleBias);
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
@@ -441,6 +531,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)
{ {

View File

@@ -0,0 +1,166 @@
// Copyright HTC Corporation All Rights Reserved.
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEngine;
public static class PackageManagerHelper
{
private static bool s_wasPreparing;
private static bool m_wasAdded;
private static bool s_wasRemoved;
private static ListRequest m_listRequest;
private static AddRequest m_addRequest;
private static RemoveRequest m_removeRequest;
private static string s_fallbackIdentifier;
public static bool isPreparingList
{
get
{
if (m_listRequest == null) { return s_wasPreparing = true; }
switch (m_listRequest.Status)
{
case StatusCode.InProgress:
return s_wasPreparing = true;
case StatusCode.Failure:
if (!s_wasPreparing)
{
Debug.LogError("Something wrong when adding package to list. error:" + m_listRequest.Error.errorCode + "(" + m_listRequest.Error.message + ")");
}
break;
case StatusCode.Success:
break;
}
return s_wasPreparing = false;
}
}
public static bool isAddingToList
{
get
{
if (m_addRequest == null) { return m_wasAdded = false; }
switch (m_addRequest.Status)
{
case StatusCode.InProgress:
return m_wasAdded = true;
case StatusCode.Failure:
if (!m_wasAdded)
{
AddRequest request = m_addRequest;
m_addRequest = null;
if (string.IsNullOrEmpty(s_fallbackIdentifier))
{
Debug.LogError("Something wrong when adding package to list. error:" + request.Error.errorCode + "(" + request.Error.message + ")");
}
else
{
Debug.Log("Failed to install package: \"" + request.Error.message + "\". Retry with fallback identifier \"" + s_fallbackIdentifier + "\"");
AddToPackageList(s_fallbackIdentifier);
}
s_fallbackIdentifier = null;
}
break;
case StatusCode.Success:
if (!m_wasAdded)
{
m_addRequest = null;
s_fallbackIdentifier = null;
ResetPackageList();
}
break;
}
return m_wasAdded = false;
}
}
public static bool isRemovingFromList
{
get
{
if (m_removeRequest == null) { return s_wasRemoved = false; }
switch (m_removeRequest.Status)
{
case StatusCode.InProgress:
return s_wasRemoved = true;
case StatusCode.Failure:
if (!s_wasRemoved)
{
var request = m_removeRequest;
m_removeRequest = null;
Debug.LogError("Something wrong when removing package from list. error:" + m_removeRequest.Error.errorCode + "(" + m_removeRequest.Error.message + ")");
}
break;
case StatusCode.Success:
if (!s_wasRemoved)
{
m_removeRequest = null;
ResetPackageList();
}
break;
}
return s_wasRemoved = false;
}
}
public static void PreparePackageList()
{
if (m_listRequest != null) { return; }
m_listRequest = Client.List(true, true);
}
public static void ResetPackageList()
{
s_wasPreparing = false;
m_listRequest = null;
}
public static bool IsPackageInList(string name, out UnityEditor.PackageManager.PackageInfo packageInfo)
{
packageInfo = null;
if (m_listRequest == null || m_listRequest.Result == null) return false;
foreach (var package in m_listRequest.Result)
{
if (package.name.Equals(name))
{
packageInfo = package;
return true;
}
}
return false;
}
public static void AddToPackageList(string identifier, string fallbackIdentifier = null)
{
Debug.Assert(m_addRequest == null);
m_addRequest = Client.Add(identifier);
s_fallbackIdentifier = fallbackIdentifier;
}
public static void RemovePackage(string identifier)
{
Debug.Assert(m_removeRequest == null);
m_removeRequest = Client.Remove(identifier);
}
public static PackageCollection GetPackageList()
{
if (m_listRequest == null || m_listRequest.Result == null)
{
return null;
}
return m_listRequest.Result;
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: a00aae5e5e4790c429467d7a03b4a6de guid: 43952515f295bac4385e71851692047d
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: ac2dcc122c218e7419c571b30aad6bbc guid: 5f1198e3724eb5b44a705edc6d6bae06
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@@ -0,0 +1,24 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using UnityEngine;
#if UNITY_EDITOR
namespace VIVE.OpenXR.Editor
{
[Serializable]
public class PreferenceAvatarAsset : ScriptableObject
{
public const string AssetPath = "Assets/VIVE/OpenXR/Preferences/PreferenceAvatarAsset.asset";
// VRM constants
public const string kVrm0Package = "UniVRM-0.109.0_7aff.unitypackage";
public const string kVrm0Asset = "Assets/VRM.meta";
public const string kVrm1Package = "VRM-0.109.0_7aff.unitypackage";
public const string kVrm1Asset = "Assets/VRM10.meta";
public bool SupportVrm0 = false;
public bool SupportVrm1 = false;
}
}
#endif

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: b048c9b388bf8d34e9814b272da7ddb5 guid: 94f6766384418a0418eb5ebdb371be20
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -0,0 +1,296 @@
// Copyright HTC Corporation All Rights Reserved.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
using System.IO;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEditor.XR.Management.Metadata;
namespace VIVE.OpenXR.Editor
{
[InitializeOnLoad]
public static class ViveOpenXRPreference
{
#region Log
static StringBuilder m_sb = null;
static StringBuilder sb {
get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
}
}
const string LOG_TAG = "VIVE.OpenXR.Editor.ViveOpenXRPreference";
static void DEBUG(StringBuilder msg) { Debug.LogFormat("{0} {1}", LOG_TAG, msg); }
static void ERROR(StringBuilder msg) { Debug.LogErrorFormat("{0} {1}", LOG_TAG, msg); }
#endregion
static ViveOpenXRPreference()
{
EditorApplication.update += OnUpdate;
}
#region Scripting Symbols
internal struct ScriptingDefinedSettings
{
public string[] scriptingDefinedSymbols;
public BuildTargetGroup[] targetGroups;
public ScriptingDefinedSettings(string[] symbols, BuildTargetGroup[] groups)
{
scriptingDefinedSymbols = symbols;
targetGroups = groups;
}
}
const string DEFINE_USE_VRM_0_x = "USE_VRM_0_x";
static readonly ScriptingDefinedSettings m_ScriptDefineSettingVrm0 = new ScriptingDefinedSettings(
new string[] { DEFINE_USE_VRM_0_x, },
new BuildTargetGroup[] { BuildTargetGroup.Android, }
);
static void AddScriptingDefineSymbols(ScriptingDefinedSettings setting)
{
for (int group_index = 0; group_index < setting.targetGroups.Length; group_index++)
{
var group = setting.targetGroups[group_index];
string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
List<string> allDefines = definesString.Split(';').ToList();
for (int symbol_index = 0; symbol_index < setting.scriptingDefinedSymbols.Length; symbol_index++)
{
if (!allDefines.Contains(setting.scriptingDefinedSymbols[symbol_index]))
{
sb.Clear().Append("AddDefineSymbols() ").Append(setting.scriptingDefinedSymbols[symbol_index]).Append(" to group ").Append(group); DEBUG(sb);
allDefines.Add(setting.scriptingDefinedSymbols[symbol_index]);
}
else
{
sb.Clear().Append("AddDefineSymbols() ").Append(setting.scriptingDefinedSymbols[symbol_index]).Append(" already existed."); DEBUG(sb);
}
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(
group,
string.Join(";", allDefines.ToArray())
);
}
}
static void RemoveScriptingDefineSymbols(ScriptingDefinedSettings setting)
{
for (int group_index = 0; group_index < setting.targetGroups.Length; group_index++)
{
var group = setting.targetGroups[group_index];
string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
List<string> allDefines = definesString.Split(';').ToList();
for (int symbol_index = 0; symbol_index < setting.scriptingDefinedSymbols.Length; symbol_index++)
{
if (allDefines.Contains(setting.scriptingDefinedSymbols[symbol_index]))
{
sb.Clear().Append("RemoveDefineSymbols() ").Append(setting.scriptingDefinedSymbols[symbol_index]).Append(" from group ").Append(group); DEBUG(sb);
allDefines.Remove(setting.scriptingDefinedSymbols[symbol_index]);
}
else
{
sb.Clear().Append("RemoveDefineSymbols() ").Append(setting.scriptingDefinedSymbols[symbol_index]).Append(" already existed."); DEBUG(sb);
}
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(
group,
string.Join(";", allDefines.ToArray())
);
}
}
static bool HasScriptingDefineSymbols(ScriptingDefinedSettings setting)
{
for (int group_index = 0; group_index < setting.targetGroups.Length; group_index++)
{
var group = setting.targetGroups[group_index];
string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
List<string> allDefines = definesString.Split(';').ToList();
for (int symbol_index = 0; symbol_index < setting.scriptingDefinedSymbols.Length; symbol_index++)
{
if (!allDefines.Contains(setting.scriptingDefinedSymbols[symbol_index]))
{
return false;
}
}
}
return true;
}
const string XR_LOADER_OPENXR_NAME = "UnityEngine.XR.OpenXR.OpenXRLoader";
internal static bool ViveOpenXRAndroidAssigned { get { return XRPackageMetadataStore.IsLoaderAssigned(XR_LOADER_OPENXR_NAME, BuildTargetGroup.Android); } }
static PreferenceAvatarAsset m_AssetAvatar = null;
static void CheckPreferenceAssets()
{
if (File.Exists(PreferenceAvatarAsset.AssetPath))
{
m_AssetAvatar = AssetDatabase.LoadAssetAtPath(PreferenceAvatarAsset.AssetPath, typeof(PreferenceAvatarAsset)) as PreferenceAvatarAsset;
}
else
{
string folderPath = PreferenceAvatarAsset.AssetPath.Substring(0, PreferenceAvatarAsset.AssetPath.LastIndexOf('/'));
DirectoryInfo folder = Directory.CreateDirectory(folderPath);
sb.Clear().Append("CheckPreferenceAssets() Creates folder: Assets/").Append(folder.Name); DEBUG(sb);
m_AssetAvatar = ScriptableObject.CreateInstance(typeof(PreferenceAvatarAsset)) as PreferenceAvatarAsset;
m_AssetAvatar.SupportVrm0 = false;
m_AssetAvatar.SupportVrm1 = false;
sb.Clear().Append("CheckPreferenceAssets() Creates the asset: ").Append(PreferenceAvatarAsset.AssetPath); DEBUG(sb);
AssetDatabase.CreateAsset(m_AssetAvatar, PreferenceAvatarAsset.AssetPath);
}
}
static void OnUpdate()
{
if (!ViveOpenXRAndroidAssigned) { return; }
CheckPreferenceAssets();
if (m_AssetAvatar)
{
// Adds the script symbol if VRM0 is imported.
if (File.Exists(PreferenceAvatarAsset.kVrm0Asset))
{
if (!HasScriptingDefineSymbols(m_ScriptDefineSettingVrm0))
{
sb.Clear().Append("OnUpdate() Adds m_ScriptDefineSettingVrm0."); DEBUG(sb);
AddScriptingDefineSymbols(m_ScriptDefineSettingVrm0);
}
m_AssetAvatar.SupportVrm0 = true;
}
else
{
if (HasScriptingDefineSymbols(m_ScriptDefineSettingVrm0))
{
sb.Clear().Append("OnUpdate() Removes m_ScriptDefineSettingVrm0."); DEBUG(sb);
RemoveScriptingDefineSymbols(m_ScriptDefineSettingVrm0);
}
m_AssetAvatar.SupportVrm0 = false;
}
m_AssetAvatar.SupportVrm1 = File.Exists(PreferenceAvatarAsset.kVrm1Asset);
}
}
#endregion
#region Preferences
const string kPreferenceName = "VIVE OpenXR";
private static GUIContent m_Vrm0Option = new GUIContent("VRM 0", "Avatar format.");
private static GUIContent m_Vrm1Option = new GUIContent("VRM 1", "Avatar format.");
internal static void ImportModule(string packagePath, bool interactive = false)
{
string target = Path.Combine("Packages/com.htc.upm.vive.openxr/UnityPackages~", packagePath);
sb.Clear().Append("ImportModule: " + target); DEBUG(sb);
AssetDatabase.ImportPackage(target, interactive);
}
static bool avatarOption = true;
#pragma warning disable 0618
[PreferenceItem(kPreferenceName)]
#pragma warning restore 0618
private static void OnPreferencesGUI()
{
if (EditorApplication.isCompiling)
{
EditorGUILayout.LabelField("Compiling...");
return;
}
if (PackageManagerHelper.isAddingToList)
{
EditorGUILayout.LabelField("Installing packages...");
return;
}
if (PackageManagerHelper.isRemovingFromList)
{
EditorGUILayout.LabelField("Removing packages...");
return;
}
PackageManagerHelper.PreparePackageList();
if (PackageManagerHelper.isPreparingList)
{
EditorGUILayout.LabelField("Checking Packages...");
return;
}
CheckPreferenceAssets();
GUIStyle sectionTitleStyle = new GUIStyle(EditorStyles.label);
sectionTitleStyle.fontSize = 16;
sectionTitleStyle.richText = true;
sectionTitleStyle.fontStyle = FontStyle.Bold;
#region Avatar
GUILayout.BeginHorizontal();
GUILayout.Space(10);
GUILayout.Label("Avatar", sectionTitleStyle);
GUILayout.EndHorizontal();
GUIStyle foldoutStyle = EditorStyles.foldout;
foldoutStyle.fontSize = 14;
foldoutStyle.fontStyle = FontStyle.Normal;
GUILayout.BeginHorizontal();
GUILayout.Space(20);
avatarOption = EditorGUILayout.Foldout(avatarOption, "Supported Format", foldoutStyle);
GUILayout.EndHorizontal();
foldoutStyle.fontSize = 12;
foldoutStyle.fontStyle = FontStyle.Normal;
if (m_AssetAvatar && avatarOption)
{
/// VRM 0
GUILayout.Space(5);
GUILayout.BeginHorizontal();
GUILayout.Space(35);
if (!m_AssetAvatar.SupportVrm0)
{
bool toggled = EditorGUILayout.ToggleLeft(m_Vrm0Option, false, GUILayout.Width(230f));
if (toggled)
{
sb.Clear().Append("OnPreferencesGUI() Adds ").Append(PreferenceAvatarAsset.kVrm0Package); DEBUG(sb);
ImportModule(PreferenceAvatarAsset.kVrm0Package);
}
}
else
{
EditorGUILayout.ToggleLeft(m_Vrm0Option, true, GUILayout.Width(230f));
}
GUILayout.EndHorizontal();
/// VRM 1
GUILayout.Space(5);
GUILayout.BeginHorizontal();
GUILayout.Space(35);
if (!m_AssetAvatar.SupportVrm1)
{
bool toggled = EditorGUILayout.ToggleLeft(m_Vrm1Option, false, GUILayout.Width(230f));
if (toggled)
{
sb.Clear().Append("OnPreferencesGUI() Adds ").Append(PreferenceAvatarAsset.kVrm1Package); DEBUG(sb);
ImportModule(PreferenceAvatarAsset.kVrm1Package);
}
}
else
{
EditorGUILayout.ToggleLeft(m_Vrm1Option, true, GUILayout.Width(230f));
}
GUILayout.EndHorizontal();
}
#endregion
}
#endregion
}
}
#endif

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 93517fb9198ee33428984693d60b4061 guid: 73bdd0b88ffae0e43a3a498347e6dea4
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -1,30 +0,0 @@
// Copyright HTC Corporation All Rights Reserved.
#if UNITY_EDITOR
using UnityEditor;
namespace VIVE.OpenXR.Editor
{
[CustomEditor(typeof(VIVEFocus3Feature))]
internal class VIVEFocus3FeatureEditor : UnityEditor.Editor
{
//private SerializedProperty enableHandTracking;
//private SerializedProperty enableTracker;
void OnEnable()
{
//enableHandTracking = serializedObject.FindProperty("enableHandTracking");
//enableTracker = serializedObject.FindProperty("enableTracker");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
//EditorGUILayout.PropertyField(enableHandTracking);
//EditorGUILayout.PropertyField(enableTracker);
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

@@ -1,30 +0,0 @@
// Copyright HTC Corporation All Rights Reserved.
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.OpenXR.Features;
namespace VIVE.OpenXR
{
[OpenXRFeatureSet(
FeatureIds = new string[] {
VIVEFocus3Feature.featureId,
VIVEFocus3Profile.featureId,
Hand.ViveHandTracking.featureId,
"vive.vive.openxr.feature.compositionlayer",
"vive.vive.openxr.feature.compositionlayer.cylinder",
"vive.vive.openxr.feature.compositionlayer.colorscalebias",
Tracker.ViveWristTracker.featureId,
Hand.ViveHandInteraction.featureId,
"vive.vive.openxr.feature.foveation",
FacialTracking.ViveFacialTracking.featureId,
},
UiName = "VIVE XR Support",
Description = "Necessary to deploy an VIVE XR compatible app.",
FeatureSetId = "com.htc.vive.openxr.featureset.vivexr",
DefaultFeatureIds = new string[] { VIVEFocus3Feature.featureId, VIVEFocus3Profile.featureId, },
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.Android }
)]
sealed class VIVEFocus3FeatureSet { }
}
#endif

View File

@@ -0,0 +1,24 @@
// Copyright HTC Corporation All Rights Reserved.
using UnityEditor;
using VIVE.OpenXR.Feature;
namespace VIVE.OpenXR.Editor
{
[CustomEditor(typeof(ViveAnchor))]
internal class ViveAnchorEditor : UnityEditor.Editor
{
private SerializedProperty enablePersistedAnchor;
void OnEnable()
{
enablePersistedAnchor = serializedObject.FindProperty("enablePersistedAnchor");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(enablePersistedAnchor);
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: f0160138780388c4aaa979212523b31f guid: 9094698271e2abb4ab295256548772c3
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -0,0 +1,44 @@
// Copyright HTC Corporation All Rights Reserved.
using UnityEngine;
using UnityEngine.SceneManagement;
#if UNITY_EDITOR
using UnityEditor;
namespace VIVE.OpenXR.Editor
{
public class ViveMenu : UnityEditor.Editor
{
private const string kMenuXR = "VIVE/XR/Convert Main Camera to ViveRig";
[MenuItem(kMenuXR, priority = 101)]
private static void ConvertToViveRig()
{
// 1. Removes default Camera
Camera cam = FindObjectOfType<Camera>();
if (cam != null && cam.transform.parent == null)
{
Debug.Log("ConvertToViveRig() remove " + cam.gameObject.name);
DestroyImmediate(cam.gameObject);
}
// 2. Loads ViveRig
if (GameObject.Find("ViveRig") == null && GameObject.Find("ViveRig(Clone)") == null)
{
GameObject prefab = Resources.Load<GameObject>("Prefabs/ViveRig");
if (prefab != null)
{
Debug.Log("ConvertToViveRig() load " + prefab.name);
GameObject inst = Instantiate(prefab, null);
if (inst != null)
{
inst.name = "ViveRig";
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
}
}
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,64 @@
// Copyright HTC Corporation All Rights Reserved.
using UnityEditor;
using UnityEngine;
using VIVE.OpenXR.Feature;
namespace VIVE.OpenXR.Editor
{
[CustomEditor(typeof(ViveMockRuntime))]
internal class ViveMockRuntimeEditor : UnityEditor.Editor
{
private SerializedProperty enableFuture;
private SerializedProperty enableAnchor;
void OnEnable()
{
enableFuture = serializedObject.FindProperty("enableFuture");
enableAnchor = serializedObject.FindProperty("enableAnchor");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
// Show a text field for description
EditorGUILayout.HelpBox("VIVE's mock runtime. Used with OpenXR MockRuntime to test unsupported extensions and features on Editor.", MessageType.Info);
if (GUILayout.Button("Install MockRuntime Library")) {
InstallMockRuntimeLibrary();
}
// check if changed
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(enableFuture);
if (EditorGUI.EndChangeCheck()) {
if (!enableFuture.boolValue) {
enableAnchor.boolValue = false;
}
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(enableAnchor);
if (EditorGUI.EndChangeCheck()) {
if (enableAnchor.boolValue) {
enableFuture.boolValue = true;
}
}
serializedObject.ApplyModifiedProperties();
}
public void InstallMockRuntimeLibrary() {
string sourcePathName = "Packages/com.htc.upm.vive.openxr/MockRuntime~/Win64/ViveMockRuntime.dll";
string destPath = "Assets/Plugins/Win64";
string destPathName = "Assets/Plugins/Win64/ViveMockRuntime.dll";
// check if the folder exists. If not, create it.
if (!System.IO.Directory.Exists(destPath)) {
System.IO.Directory.CreateDirectory(destPath);
}
FileUtil.CopyFileOrDirectory(sourcePathName, destPathName);
AssetDatabase.Refresh();
}
}
}

View File

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

View File

@@ -1,135 +0,0 @@
// Copyright HTC Corporation All Rights Reserved.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.Management.Metadata;
namespace VIVE.OpenXR.Editor
{
[InitializeOnLoad]
public static class CheckIfVIVEEnabled
{
const string LOG_TAG = "VIVE.OpenXR.Editor.CheckIfVIVEEnabled";
static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
const string VERSION_DEFINE_OPENXR = "USE_VIVE_OPENXR_1_0_0";
internal struct ScriptingDefinedSettings
{
public string[] scriptingDefinedSymbols;
public BuildTargetGroup[] targetGroups;
public ScriptingDefinedSettings(string[] symbols, BuildTargetGroup[] groups)
{
scriptingDefinedSymbols = symbols;
targetGroups = groups;
}
}
static readonly ScriptingDefinedSettings m_ScriptDefineSettingOpenXRAndroid = new ScriptingDefinedSettings(
new string[] { VERSION_DEFINE_OPENXR, },
new BuildTargetGroup[] { BuildTargetGroup.Android, }
);
const string XR_LOADER_OPENXR_NAME = "UnityEngine.XR.OpenXR.OpenXRLoader";
internal static bool ViveOpenXRAndroidAssigned { get { return XRPackageMetadataStore.IsLoaderAssigned(XR_LOADER_OPENXR_NAME, BuildTargetGroup.Android); } }
static void AddScriptingDefineSymbols(ScriptingDefinedSettings setting)
{
for (int group_index = 0; group_index < setting.targetGroups.Length; group_index++)
{
var group = setting.targetGroups[group_index];
string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
List<string> allDefines = definesString.Split(';').ToList();
for (int symbol_index = 0; symbol_index < setting.scriptingDefinedSymbols.Length; symbol_index++)
{
if (!allDefines.Contains(setting.scriptingDefinedSymbols[symbol_index]))
{
DEBUG("AddDefineSymbols() " + setting.scriptingDefinedSymbols[symbol_index] + " to group " + group);
allDefines.Add(setting.scriptingDefinedSymbols[symbol_index]);
}
else
{
DEBUG("AddDefineSymbols() " + setting.scriptingDefinedSymbols[symbol_index] + " already existed.");
}
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(
group,
string.Join(";", allDefines.ToArray())
);
}
}
static void RemoveScriptingDefineSymbols(ScriptingDefinedSettings setting)
{
for (int group_index = 0; group_index < setting.targetGroups.Length; group_index++)
{
var group = setting.targetGroups[group_index];
string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
List<string> allDefines = definesString.Split(';').ToList();
for (int symbol_index = 0; symbol_index < setting.scriptingDefinedSymbols.Length; symbol_index++)
{
if (allDefines.Contains(setting.scriptingDefinedSymbols[symbol_index]))
{
DEBUG("RemoveDefineSymbols() " + setting.scriptingDefinedSymbols[symbol_index] + " from group " + group);
allDefines.Remove(setting.scriptingDefinedSymbols[symbol_index]);
}
else
{
DEBUG("RemoveDefineSymbols() " + setting.scriptingDefinedSymbols[symbol_index] + " already existed.");
}
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(
group,
string.Join(";", allDefines.ToArray())
);
}
}
static bool HasScriptingDefineSymbols(ScriptingDefinedSettings setting)
{
for (int group_index = 0; group_index < setting.targetGroups.Length; group_index++)
{
var group = setting.targetGroups[group_index];
string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
List<string> allDefines = definesString.Split(';').ToList();
for (int symbol_index = 0; symbol_index < setting.scriptingDefinedSymbols.Length; symbol_index++)
{
if (!allDefines.Contains(setting.scriptingDefinedSymbols[symbol_index]))
{
return false;
}
}
}
return true;
}
static void CheckScriptingDefineSymbols()
{
// Adds the script symbol if Vive OpenXR Plugin - Android is imported and assigned in XR Plugin-in Management.
if (ViveOpenXRAndroidAssigned)
{
if (!HasScriptingDefineSymbols(m_ScriptDefineSettingOpenXRAndroid))
{
DEBUG("OnUpdate() Adds m_ScriptDefineSettingOpenXRAndroid.");
AddScriptingDefineSymbols(m_ScriptDefineSettingOpenXRAndroid);
}
}
// Removes the script symbol if Vive OpenXR Plugin - Android is uninstalled.
else
{
if (HasScriptingDefineSymbols(m_ScriptDefineSettingOpenXRAndroid))
{
DEBUG("OnUpdate() Removes m_ScriptDefineSettingOpenXRAndroid.");
RemoveScriptingDefineSymbols(m_ScriptDefineSettingOpenXRAndroid);
}
}
}
static void OnUpdate()
{
//CheckScriptingDefineSymbols();
}
static CheckIfVIVEEnabled()
{
EditorApplication.update += OnUpdate;
}
}
}
#endif

View File

@@ -0,0 +1,45 @@
// Copyright HTC Corporation All Rights Reserved.
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.OpenXR.Features;
namespace VIVE.OpenXR
{
[OpenXRFeatureSet(
FeatureIds = new string[] {
VIVEFocus3Feature.featureId,
VIVEFocus3Profile.featureId,
Hand.ViveHandTracking.featureId,
"vive.openxr.feature.compositionlayer",
"vive.openxr.feature.compositionlayer.cylinder",
"vive.openxr.feature.compositionlayer.colorscalebias",
Tracker.ViveWristTracker.featureId,
Hand.ViveHandInteraction.featureId,
"vive.openxr.feature.foveation",
FacialTracking.ViveFacialTracking.featureId,
PlaneDetection.VivePlaneDetection.featureId,
VivePathEnumeration.featureId,
Feature.ViveAnchor.featureId,
DisplayRefreshRate.ViveDisplayRefreshRate.featureId,
Passthrough.VivePassthrough.featureId,
FirstPersonObserver.ViveFirstPersonObserver.FeatureId,
SecondaryViewConfiguration.ViveSecondaryViewConfiguration.FeatureId,
UserPresence.ViveUserPresence.featureId,
CompositionLayer.ViveCompositionLayerExtraSettings.featureId,
FrameSynchronization.ViveFrameSynchronization.featureId,
EyeTracker.ViveEyeTracker.featureId,
Feature.ViveMockRuntime.featureId,
Interaction.ViveInteractions.featureId,
},
UiName = "VIVE XR Support",
Description = "Necessary to deploy an VIVE XR compatible app.",
FeatureSetId = "com.htc.vive.openxr.featureset.vivexr",
#if UNITY_ANDROID
DefaultFeatureIds = new string[] { VIVEFocus3Feature.featureId, VIVEFocus3Profile.featureId, },
#endif
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone }
)]
sealed class ViveOpenXRFeatureSet { }
}
#endif

View File

@@ -0,0 +1,81 @@
using System;
using System.Reflection;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEditor.XR.OpenXR.Features;
using UnityEngine.XR.OpenXR.Features;
using UnityEngine.XR.OpenXR;
using static VIVE.OpenXR.VIVEFocus3Feature;
namespace VIVE.OpenXR.Editor
{
public class ViveSpectatorCameraProcess : OpenXRFeatureBuildHooks
{
public override int callbackOrder => 1;
public override Type featureType => typeof(VIVEFocus3Feature);
/// <summary>
/// Enable or disable the "First Person Observer" extension according to the Spectator Camera Feature.
/// </summary>
/// <param name="enable">Type True if Spectator Camera Feature is enabled. Otherwise, type False.</param>
private static void SetFirstPersonObserver(in bool enable)
{
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
foreach (OpenXRFeature feature in settings.GetFeatures<OpenXRFeature>())
{
FieldInfo fieldInfoOpenXrExtensionStrings = typeof(OpenXRFeature).GetField(
"openxrExtensionStrings",
BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfoOpenXrExtensionStrings != null)
{
var openXrExtensionStringsArray =
((string)fieldInfoOpenXrExtensionStrings.GetValue(feature)).Split(' ');
foreach (var stringItem in openXrExtensionStringsArray)
{
if (string.IsNullOrEmpty(stringItem))
{
continue;
}
if (!string.Equals(stringItem, FirstPersonObserver.ViveFirstPersonObserver.OPEN_XR_EXTENSION_STRING))
{
continue;
}
feature.enabled = enable;
return;
}
}
}
}
#region The callbacks during the build process when your OpenXR Extension is enabled.
protected override void OnPreprocessBuildExt(BuildReport report)
{
if (IsViveSpectatorCameraEnabled())
{
SetFirstPersonObserver(true);
UnityEngine.Debug.Log("Enable \"First Person Observer\" extension due to the Spectator Camera Feature.");
}
else
{
SetFirstPersonObserver(false);
UnityEngine.Debug.Log("Disable \"First Person Observer\" extension because Spectator Camera Feature is closed.");
}
}
protected override void OnPostGenerateGradleAndroidProjectExt(string path)
{
}
protected override void OnPostprocessBuildExt(BuildReport report)
{
}
#endregion
}
}

View File

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

View File

@@ -1,54 +0,0 @@
================================================================================
Copyright 2017-2021, HTC Corporation. All rights reserved.
================================================================================
Unless otherwise provided herein or in the folder you download or use, the information in this Work is the exclusive property of HTC.
Please note that this Work includes VIVE SDK native binary which is subject to a sperate license agreement described below. You can find more detailed information about VIVE SDK and its available plugin software packages at VIVE developer resource page (https://developer.vive.com/resources/knowledgebase/wave-sdk/).
*VIVE SDK native binary:
Your use of VIVE SDK native binary will be subject to SDK License Agreement between you and HTC. You can find the text of license agreement at https://developer.vive.com/resources/knowledgebase/sdk-license-agreement-english-version/. Please read it carefully before using this Work.
*VIVE SDK Plugin Package:
Your use of plugin software package will be subject to the license terms contemplated herein. You can use, modify, share and/or reproduce the VIVE SDK Plugin Package in accordance with the Agreement herein.
If you do not agree to the terms of the Agreement, please do not use this Work.
The VIVE SDK native binary contains some third party software which separate license terms may apply. Please refer to the Accompanying License in a separate file named “VIVE SDK Native Binary Accompanying OSS License”.
================================================================================
License Terms for VIVE SDK Plugin Package
The works ("Work") herein refer to the software developed or owned by
HTC Corporation ("HTC") under the terms of the license. Unless otherwise
provided herein or in the folder you download or use, the information in
this Work is the exclusive property of HTC. HTC grants the
legal user the right to use the Work within the scope of the legitimate
development of software. No further right is granted under this license,
including but not limited to, distribution, reproduction and
modification. Any other usage of the Works shall be subject to the
written consent of HTC.
The use of the Work is permitted provided that the following conditions
are met:
* The Work is used in a source code form must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* The Work is used in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distributions.
* Neither HTC nor the names of its contributors may be used to
endorse or promote products derived from this software without
specific prior written permission.
THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER
DEALINGS IN THE WORK.

View 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/

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 28cd706d4fe07ba4c85df80c80bbdee6 guid: d55a23fb1f9c7b0479d1f15716620072
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@@ -0,0 +1,38 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Runtime.InteropServices;
namespace VIVE.OpenXR.Feature
{
public interface IViveFeatureWrapper
{
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr xrGetInstanceProcAddr);
public void OnInstanceDestroy();
}
public class ViveFeatureWrapperBase<T> where T : ViveFeatureWrapperBase<T>, new()
{
private static readonly Lazy<T> lazyInstance = new Lazy<T>(() => new T());
public static T Instance => lazyInstance.Value;
// Set true in yourfeature's OnInstanceCreate
public bool IsInited { get; protected set; } = false;
public OpenXRHelper.xrGetInstanceProcAddrDelegate xrGetInstanceProcAddr;
/// <summary>
/// Complete the xrGetInstanceProcAddr by set the pointer received in OnInstanceCreate
/// </summary>
/// <param name="intPtr"></param>
public void SetGetInstanceProcAddrPtr(IntPtr intPtr)
{
if (intPtr == null || intPtr == IntPtr.Zero)
throw new Exception("xrGetInstanceProcAddr is null");
xrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrGetInstanceProcAddrDelegate>(intPtr);
}
}
}

View File

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

View File

@@ -0,0 +1,80 @@
using System;
using System.Runtime.InteropServices;
namespace VIVE.OpenXR
{
public static class MemoryTools
{
/// <summary>
/// Convert the enum array to IntPtr. Should call <see cref="ReleaseRawMemory(IntPtr)"/> after use.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Make the same size raw buffer from input array.
/// </summary>
/// <typeparam name="T">Data type could be primitive type or struct. Should call <see cref="ReleaseRawMemory(IntPtr)"/> after use.</typeparam>
/// <param name="refArray">The data array</param>
/// <returns>The memory handle. Should release by <see cref="ReleaseRawMemory(IntPtr)"/></returns>
public static IntPtr MakeRawMemory<T>(T[] refArray)
{
int size = Marshal.SizeOf(typeof(T)) * refArray.Length;
return Marshal.AllocHGlobal(size);
}
/// <summary>
/// Copy the raw memory to the array. You should make sure the array has the same size as the raw memory.
/// </summary>
/// <typeparam name="T">Convert the memory to this type array.</typeparam>
/// <param name="array">The output array.</param>
/// <param name="raw">The data source in raw memory form.</param>
/// <param name="count">Specify the copy count. Count should be less than array length.</param>
public static void CopyFromRawMemory<T>(T[] array, IntPtr raw, int count = 0)
{
int N = array.Length;
if (count > 0 && count < array.Length)
N = count;
int step = Marshal.SizeOf(typeof(T));
for (int i = 0; i < N; i++)
{
array[i] = Marshal.PtrToStructure<T>(IntPtr.Add(raw, i * step));
}
}
/// <summary>
/// Make the same size raw buffer from input array. Make sure the raw has enough size.
/// </summary>
/// <typeparam name="T">Convert this type array to raw memory.</typeparam>
/// <param name="raw">The output data in raw memory form</param>
/// <param name="array">The data source</param>
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);
}
}
/// <summary>
/// Release the raw memory handle which is created by <see cref="MakeRawMemory{T}(T[])"/>
/// </summary>
/// <param name="ptr"></param>
public static void ReleaseRawMemory(IntPtr ptr)
{
Marshal.FreeHGlobal(ptr);
}
}
}

View File

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

View File

@@ -0,0 +1,243 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Profiling;
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 : ViveFeatureWrapperBase<CommonWrapper>, IViveFeatureWrapper
{
OpenXRHelper.xrGetSystemPropertiesDelegate XrGetSystemProperties;
OpenXRHelper.xrCreateSwapchainDelegate XrCreateSwapchain;
OpenXRHelper.xrDestroySwapchainDelegate XrDestroySwapchain;
OpenXRHelper.xrEnumerateSwapchainFormatsDelegate XrEnumerateSwapchainFormats;
OpenXRHelper.xrEnumerateSwapchainImagesDelegate XrEnumerateSwapchainImages;
OpenXRHelper.xrWaitSwapchainImageDelegate XrWaitSwapchainImage;
OpenXRHelper.xrAcquireSwapchainImageDelegate XrAcquireSwapchainImage;
OpenXRHelper.xrReleaseSwapchainImageDelegate XrReleaseSwapchainImage;
/// <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 xrGetInstanceProcAddrPtr)
{
if (IsInited) return true;
if (xrInstance == 0)
throw new Exception("CommonWrapper: xrInstance is null");
Debug.Log("CommonWrapper: OnInstanceCreate()");
SetGetInstanceProcAddrPtr(xrGetInstanceProcAddrPtr);
bool ret = true;
IntPtr funcPtr = IntPtr.Zero;
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrGetSystemProperties", out XrGetSystemProperties);
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrCreateSwapchain", out XrCreateSwapchain);
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrDestroySwapchain", out XrDestroySwapchain);
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrEnumerateSwapchainFormats", out XrEnumerateSwapchainFormats);
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrEnumerateSwapchainImages", out XrEnumerateSwapchainImages);
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrWaitSwapchainImage", out XrWaitSwapchainImage);
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrAcquireSwapchainImage", out XrAcquireSwapchainImage);
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrReleaseSwapchainImage", out XrReleaseSwapchainImage);
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;
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;
}
public XrResult CreateSwapchain(XrSession session, ref XrSwapchainCreateInfo createInfo, out XrSwapchain swapchain)
{
if (IsInited == false || XrCreateSwapchain == null)
{
swapchain = default;
return XrResult.XR_ERROR_HANDLE_INVALID;
}
return XrCreateSwapchain(session, ref createInfo, out swapchain);
}
public XrResult DestroySwapchain(XrSwapchain swapchain)
{
if (IsInited == false || XrDestroySwapchain == null)
{
return XrResult.XR_ERROR_HANDLE_INVALID;
}
return XrDestroySwapchain(swapchain);
}
public XrResult EnumerateSwapchainFormats(XrSession session, uint formatCapacityInput, ref uint formatCountOutput, ref long[] formats)
{
if (IsInited == false || XrEnumerateSwapchainFormats == null)
{
formatCountOutput = 0;
return XrResult.XR_ERROR_HANDLE_INVALID;
}
if (formatCapacityInput != 0 && (formats == null || formats.Length < formatCapacityInput))
return XrResult.XR_ERROR_SIZE_INSUFFICIENT;
if (formatCapacityInput == 0)
{
Debug.Log("CommonWrapper: EnumerateSwapchainFormats(ci=" + formatCapacityInput + ")");
return XrEnumerateSwapchainFormats(session, 0, ref formatCountOutput, IntPtr.Zero);
}
else
{
Debug.Log("CommonWrapper: EnumerateSwapchainFormats(ci=" + formatCapacityInput + ", formats=long[" + formats.Length + "])");
IntPtr formatsPtr = MemoryTools.MakeRawMemory(formats);
var ret = XrEnumerateSwapchainFormats(session, formatCapacityInput, ref formatCountOutput, formatsPtr);
if (ret == XrResult.XR_SUCCESS)
MemoryTools.CopyFromRawMemory(formats, formatsPtr, (int)formatCountOutput);
MemoryTools.ReleaseRawMemory(formatsPtr);
return ret;
}
}
public XrResult EnumerateSwapchainImages(XrSwapchain swapchain, uint imageCapacityInput, ref uint imageCountOutput, IntPtr imagesPtr)
{
if (IsInited == false || XrEnumerateSwapchainImages == null)
{
imageCountOutput = 0;
return XrResult.XR_ERROR_HANDLE_INVALID;
}
return XrEnumerateSwapchainImages(swapchain, imageCapacityInput, ref imageCountOutput, imagesPtr);
}
[DllImport("viveopenxr", EntryPoint = "CwAcquireSwapchainImage")]
public static extern XrResult CwAcquireSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageAcquireInfo acquireInfo, out uint index);
public XrResult AcquireSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageAcquireInfo acquireInfo, out uint index)
{
if (IsInited == false || XrAcquireSwapchainImage == null)
{
index = 0;
return XrResult.XR_ERROR_HANDLE_INVALID;
}
Profiler.BeginSample("ASW: xrAcqScImg");
var res = XrAcquireSwapchainImage(swapchain, ref acquireInfo, out index);
Profiler.EndSample();
return res;
}
[DllImport("viveopenxr", EntryPoint = "CwWaitSwapchainImage")]
public static extern XrResult CwWaitSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageWaitInfo waitInfo);
public XrResult WaitSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageWaitInfo waitInfo)
{
if (IsInited == false || XrWaitSwapchainImage == null)
{
return XrResult.XR_ERROR_HANDLE_INVALID;
}
Profiler.BeginSample("ASW: xrWaitScImg");
var res = XrWaitSwapchainImage(swapchain, ref waitInfo);
Profiler.EndSample();
return res;
}
[DllImport("viveopenxr", EntryPoint = "CwReleaseSwapchainImage")]
public static extern XrResult CwReleaseSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageReleaseInfo releaseInfo);
public XrResult ReleaseSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageReleaseInfo releaseInfo)
{
if (IsInited == false || XrReleaseSwapchainImage == null)
{
return XrResult.XR_ERROR_HANDLE_INVALID;
}
// Add Profiler
Profiler.BeginSample("ASW: xrRelScImg");
var res = XrReleaseSwapchainImage(swapchain, ref releaseInfo);
Profiler.EndSample();
return res;
}
}
}

View File

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

View File

@@ -0,0 +1,207 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using UnityEngine;
using UnityEngine.XR.OpenXR;
namespace VIVE.OpenXR.Feature
{
using XrFutureEXT = System.IntPtr;
/// <summary>
/// To use this wrapper,
/// 1. Add the "XR_EXT_Future" extension to the instance's enabled extensions list.
/// 2. Call FutureWrapper.Instance.OnInstanceCreate() in your feature's OnInstanceCreate().
/// 3. Call FutureWrapper.Instance.OnInstanceDestroy() in your feature's OnInstanceDestroy().
///
/// <see cref="VIVE.OpenXR.Toolkits.FutureTask.Poll"/> function helps make async Task.
/// </summary>
public class FutureWrapper : ViveFeatureWrapperBase<FutureWrapper>, IViveFeatureWrapper
{
public enum XrFutureStateEXT
{
None = 0, // Not defined in extension. A default value.
Pending = 1,
Ready = 2,
MAX = 0x7FFFFFFF
}
public struct XrFuturePollInfoEXT {
public XrStructureType type; // XR_TYPE_FUTURE_POLL_INFO_EXT
public IntPtr next;
public XrFutureEXT future;
}
public struct XrFuturePollResultEXT {
public XrStructureType type; // XR_TYPE_FUTURE_POLL_RESULT_EXT
public IntPtr next;
public XrFutureStateEXT state;
}
public struct XrFutureCancelInfoEXT
{
public XrStructureType type; // XR_TYPE_FUTURE_CANCEL_INFO_EXT
public IntPtr next;
public XrFutureEXT future;
}
public struct XrFutureCompletionBaseHeaderEXT
{
public XrStructureType type; // XR_TYPE_FUTURE_COMPLETION_EXT
public IntPtr next;
public XrResult futureResult;
}
public struct XrFutureCompletionEXT
{
public XrStructureType type; // XR_TYPE_FUTURE_COMPLETION_EXT
public IntPtr next;
public XrResult futureResult;
}
public delegate XrResult XrPollFutureEXTDelegate(XrInstance instance, ref XrFuturePollInfoEXT pollInfo, out XrFuturePollResultEXT pollResult);
public delegate XrResult XrCancelFutureEXTDelegate(XrInstance instance, ref XrFutureCancelInfoEXT cancelInfo);
XrPollFutureEXTDelegate XrPollFutureEXT;
XrCancelFutureEXTDelegate XrCancelFutureEXT;
XrInstance xrInstance;
/// <summary>
/// Features should call FutureWrapper.Instance.OnInstanceCreate() in their OnInstanceCreate().
/// </summary>
/// <param name="xrInstance"></param>
/// <param name="xrGetInstanceProcAddrPtr"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrPtr)
{
if (IsInited) return true;
if (xrInstance == null)
throw new Exception("FutureWrapper: xrInstance is null");
this.xrInstance = xrInstance;
if (xrGetInstanceProcAddrPtr == null)
throw new Exception("FutureWrapper: xrGetInstanceProcAddr is null");
SetGetInstanceProcAddrPtr(xrGetInstanceProcAddrPtr);
Debug.Log("FutureWrapper: OnInstanceCreate()");
bool hasFuture = OpenXRRuntime.IsExtensionEnabled("XR_EXT_future");
if (!hasFuture)
{
Debug.LogError("FutureWrapper: XR_EXT_future is not enabled. Check your feature's kOpenxrExtensionString.");
return false;
}
bool ret = true;
IntPtr funcPtr = IntPtr.Zero;
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrPollFutureEXT", out XrPollFutureEXT);
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrCancelFutureEXT", out XrCancelFutureEXT);
if (!ret)
{
Debug.LogError("FutureWrapper: Failed to get function pointer.");
return false;
}
IsInited = ret;
return ret;
}
public void OnInstanceDestroy()
{
Debug.Log("FutureWrapper: OnInstanceDestroy()");
IsInited = false;
XrPollFutureEXT = null;
XrCancelFutureEXT = null;
xrInstance = 0;
}
/// <summary>
/// Used to get the state of a future. If Ready, Call complete functions to get the result.
/// </summary>
/// <param name="pollInfo"></param>
/// <param name="pollResult"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public XrResult PollFuture(ref XrFuturePollInfoEXT pollInfo, out XrFuturePollResultEXT pollResult)
{
pollResult= new XrFuturePollResultEXT()
{
type = XrStructureType.XR_TYPE_FUTURE_POLL_RESULT_EXT,
next = IntPtr.Zero,
state = XrFutureStateEXT.None
};
if (!IsInited)
return XrResult.XR_ERROR_HANDLE_INVALID;
return XrPollFutureEXT(xrInstance, ref pollInfo, out pollResult);
}
/// <summary>
/// Used to get the state of a future. If Ready, Call complete functions to get the result.
/// </summary>
/// <param name="future"></param>
/// <param name="pollResult"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public XrResult PollFuture(XrFutureEXT future, out XrFuturePollResultEXT pollResult)
{
pollResult = new XrFuturePollResultEXT()
{
type = XrStructureType.XR_TYPE_FUTURE_POLL_RESULT_EXT,
next = IntPtr.Zero,
state = XrFutureStateEXT.None
};
if (!IsInited)
return XrResult.XR_ERROR_HANDLE_INVALID;
XrFuturePollInfoEXT pollInfo = new XrFuturePollInfoEXT()
{
type = XrStructureType.XR_TYPE_FUTURE_POLL_INFO_EXT,
next = IntPtr.Zero,
future = future
};
return XrPollFutureEXT(xrInstance, ref pollInfo, out pollResult);
}
/// <summary>
/// This function cancels the future and signals that the async operation is not required.
/// After a future has been cancelled any functions using this future must return XR_ERROR_FUTURE_INVALID_EXT.
/// </summary>
/// <param name="cancelInfo"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public XrResult CancelFuture(ref XrFutureCancelInfoEXT cancelInfo)
{
if (!IsInited)
return XrResult.XR_ERROR_HANDLE_INVALID;
return XrCancelFutureEXT(xrInstance, ref cancelInfo);
}
/// <summary>
/// <see cref="CancelFuture(ref XrFutureCancelInfoEXT)"/>
/// </summary>
/// <param name="future"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public XrResult CancelFuture(XrFutureEXT future)
{
if (!IsInited)
return XrResult.XR_ERROR_HANDLE_INVALID;
XrFutureCancelInfoEXT cancelInfo = new XrFutureCancelInfoEXT()
{
type = XrStructureType.XR_TYPE_FUTURE_CANCEL_INFO_EXT,
next = IntPtr.Zero,
future = future
};
return XrCancelFutureEXT(xrInstance, ref cancelInfo);
}
}
}

View File

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

View File

@@ -0,0 +1,196 @@
// Copyright HTC Corporation All Rights Reserved.
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 : ViveFeatureWrapperBase<SpaceWrapper>, IViveFeatureWrapper
{
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, IntPtr GetAddr)
{
if (IsInited) return true;
if (xrInstance == null)
throw new Exception("ViveSpace: xrInstance is null");
SetGetInstanceProcAddrPtr(GetAddr);
Debug.Log("ViveSpace: OnInstanceCreate()");
bool ret = true;
IntPtr funcPtr = IntPtr.Zero;
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrCreateReferenceSpace", out XrCreateReferenceSpace);
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrLocateSpace", out XrLocateSpace);
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, 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)
{
space = 0;
if (!IsInited)
return XrResult.XR_ERROR_HANDLE_INVALID;
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)
{
space = 0;
if (!IsInited)
return XrResult.XR_ERROR_HANDLE_INVALID;
return XrCreateReferenceSpace(session, ref createInfo, out space);
}
public XrResult LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location)
{
if (!IsInited)
return XrResult.XR_ERROR_HANDLE_INVALID;
//Debug.Log($"LocateSpace(s={space}, bs={baseSpace}, t={time}");
return XrLocateSpace(space, baseSpace, time, ref location);
}
public XrResult DestroySpace(XrSpace space)
{
if (!IsInited)
return XrResult.XR_ERROR_HANDLE_INVALID;
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 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);
}
}
}

View File

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

View File

@@ -0,0 +1,182 @@
// Copyright HTC Corporation All Rights Reserved.
using System.Runtime.InteropServices;
using System;
using UnityEngine;
using AOT;
using System.Collections.Generic;
using System.Text;
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 ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
/// }
/// </summary>
partial class ViveInterceptors
{
public const string TAG = "VIVE.OpenXR.ViveInterceptors";
static StringBuilder m_sb = null;
static StringBuilder sb {
get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
}
}
static void DEBUG(StringBuilder msg) { Debug.LogFormat("{0} {1}", TAG, msg); }
static void ERROR(StringBuilder msg) { Debug.LogErrorFormat("{0} {1}", TAG, msg); }
public static ViveInterceptors instance = null;
public static ViveInterceptors Instance
{
get
{
if (instance == null)
instance = new ViveInterceptors();
return instance;
}
}
public ViveInterceptors()
{
Debug.Log("ViveInterceptors");
}
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)
{
// Used to check if the original function is already hooked.
if (instance == 0 && name == "ViveInterceptorHooked")
{
function = IntPtr.Zero;
return XrResult.XR_SUCCESS;
}
// Custom interceptors
if (name == "xrWaitFrame" && requiredFunctions.Contains(name))
{
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;
}
if (name == "xrEndFrame" && requiredFunctions.Contains(name))
{
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
if (ret == XrResult.XR_SUCCESS)
{
XrEndFrameOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrEndFrame>(function);
function = xrEndFrameInterceptorPtr;
}
return ret;
}
#if PERFORMANCE_TEST
if (name == "xrLocateSpace" && requiredFunctions.Contains(name))
{
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
if (ret == XrResult.XR_SUCCESS)
{
XrLocateSpaceOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrLocateSpace>(function);
function = xrLocateSpaceInterceptorPtr;
}
return ret;
}
#endif
if (name == "xrPollEvent" && requiredFunctions.Contains(name))
{
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
if (ret == XrResult.XR_SUCCESS)
{
xrPollEventOrigin = Marshal.GetDelegateForFunctionPointer < xrPollEventDelegate > (function);
function = xrPollEventPtr;
}
return ret;
}
if (name == "xrBeginSession" && requiredFunctions.Contains(name))
{
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
if (ret == XrResult.XR_SUCCESS)
{
xrBeginSessionOrigin = Marshal.GetDelegateForFunctionPointer<xrBeginSessionDelegate>(function);
function = xrBeginSessionPtr;
}
return ret;
}
return XrGetInstanceProcAddrOriginal(instance, name, out function);
}
public IntPtr HookGetInstanceProcAddr(IntPtr func)
{
Debug.Log($"{TAG}: HookGetInstanceProcAddr");
if (XrGetInstanceProcAddrOriginal == null)
{
Debug.Log($"{TAG}: registering our own xrGetInstanceProcAddr");
XrGetInstanceProcAddrOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrGetInstanceProcAddr>(func);
#if UNITY_EDITOR
if (Application.isEditor) {
// This is a trick to check if the original function is already hooked by this class. Sometimes, the static XrGetInstanceProcAddrOriginal didn't work as expected.
Debug.Log($"{TAG}: Check if duplicate hooked by this script with instance=0 and \"ViveInterceptorHooked\" name. If following a loader error, ignore it.");
// E OpenXR-Loader: Error [SPEC | xrGetInstanceProcAddr | VUID-xrGetInstanceProcAddr-instance-parameter] : XR_NULL_HANDLE for instance but query for ViveInterceptorHooked requires a valid instance
// Call XrGetInstanceProcAddrOriginal to check if the original function is already hooked by this class
if (XrGetInstanceProcAddrOriginal(0, "ViveInterceptorHooked", out IntPtr function) == XrResult.XR_SUCCESS)
{
// If it is called successfully, it means the original function is already hooked. So we should return the original function.
Debug.Log($"{TAG}: Already hooked");
return func;
}
}
#endif
return hookGetInstanceProcAddrHandlePtr;
}
else
{
// Dont return hookGetInstanceProcAddrHandlePtr again.
// If this hook function is called by multiple features, it should only work at the first time.
// If called by other features, it should return the original function.
return func;
}
}
static readonly List<string> requiredFunctions = new List<string>();
/// <summary>
/// Call before <see cref="HookGetInstanceProcAddr" /> to add required functions."/>
/// </summary>
/// <param name="name"></param>
public void AddRequiredFunction(string name)
{
if (requiredFunctions.Contains(name)) return;
Debug.Log($"{TAG}: AddRequiredFunction({name})");
requiredFunctions.Add(name);
}
}
}

View File

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

View File

@@ -0,0 +1,85 @@
// Copyright HTC Corporation All Rights Reserved.
#define DEBUG
using AOT;
using System;
using System.Runtime.InteropServices;
using UnityEngine.Profiling;
using VIVE.OpenXR.FrameSynchronization;
namespace VIVE.OpenXR
{
partial class ViveInterceptors
{
#region xrBeginSession
public delegate XrResult xrBeginSessionDelegate(XrSession session, ref XrSessionBeginInfo beginInfo);
private static xrBeginSessionDelegate xrBeginSessionOrigin = null;
[MonoPInvokeCallback(typeof(xrBeginSessionDelegate))]
private static XrResult xrBeginSessionInterceptor(XrSession session, ref XrSessionBeginInfo beginInfo)
{
Profiler.BeginSample("ViveInterceptors:BeginSession");
XrResult result = XrResult.XR_ERROR_FUNCTION_UNSUPPORTED;
if (xrBeginSessionOrigin != null)
{
if (m_EnableFrameSynchronization)
{
frameSynchronizationSessionBeginInfo.mode = m_FrameSynchronizationMode;
frameSynchronizationSessionBeginInfo.next = beginInfo.next;
beginInfo.next = Marshal.AllocHGlobal(Marshal.SizeOf(frameSynchronizationSessionBeginInfo));
long offset = 0;
if (IntPtr.Size == 4)
offset = beginInfo.next.ToInt32();
else
offset = beginInfo.next.ToInt64();
IntPtr frame_synchronization_session_begin_info_ptr = new IntPtr(offset);
Marshal.StructureToPtr(frameSynchronizationSessionBeginInfo, frame_synchronization_session_begin_info_ptr, false);
#if DEBUG
if (IntPtr.Size == 4)
offset = beginInfo.next.ToInt32();
else
offset = beginInfo.next.ToInt64();
IntPtr fs_begin_info_ptr = new IntPtr(offset);
XrFrameSynchronizationSessionBeginInfoHTC fsBeginInfo = (XrFrameSynchronizationSessionBeginInfoHTC)Marshal.PtrToStructure(fs_begin_info_ptr, typeof(XrFrameSynchronizationSessionBeginInfoHTC));
sb.Clear().Append("xrBeginSessionInterceptor() beginInfo.next = (").Append(fsBeginInfo.type).Append(", ").Append(fsBeginInfo.mode).Append(")"); DEBUG(sb);
#endif
}
result = xrBeginSessionOrigin(session, ref beginInfo);
}
else
{
sb.Clear().Append("xrBeginSessionInterceptor() Not assign xrBeginSession!"); ERROR(sb);
}
Profiler.EndSample();
return result;
}
private static readonly xrBeginSessionDelegate xrBeginSession = new xrBeginSessionDelegate(xrBeginSessionInterceptor);
private static readonly IntPtr xrBeginSessionPtr = Marshal.GetFunctionPointerForDelegate(xrBeginSession);
#endregion
private static XrFrameSynchronizationSessionBeginInfoHTC frameSynchronizationSessionBeginInfo = XrFrameSynchronizationSessionBeginInfoHTC.identity;
private static bool m_EnableFrameSynchronization = false;
private static XrFrameSynchronizationModeHTC m_FrameSynchronizationMode = XrFrameSynchronizationModeHTC.XR_FRAME_SYNCHRONIZATION_MODE_STABILIZED_HTC;
/// <summary>
/// Activate or deactivate the Frame Synchronization feature.
/// </summary>
/// <param name="active">True for activate</param>
/// <param name="mode">The <see cref="XrFrameSynchronizationModeHTC"/> used for Frame Synchronization.</param>
public void ActivateFrameSynchronization(bool active, XrFrameSynchronizationModeHTC mode)
{
m_EnableFrameSynchronization = active;
m_FrameSynchronizationMode = mode;
sb.Clear().Append("ActivateFrameSynchronization() ").Append(active ? "enable " : "disable ").Append(mode); DEBUG(sb);
}
}
}

View File

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

View File

@@ -0,0 +1,129 @@
// Copyright HTC Corporation All Rights Reserved.
using AOT;
using System;
using System.Runtime.InteropServices;
using UnityEngine.Profiling;
using VIVE.OpenXR.DisplayRefreshRate;
using VIVE.OpenXR.Passthrough;
using VIVE.OpenXR.UserPresence;
namespace VIVE.OpenXR
{
partial class ViveInterceptors
{
#region xrPollEvent
public delegate XrResult xrPollEventDelegate(XrInstance instance, ref XrEventDataBuffer eventData);
private static xrPollEventDelegate xrPollEventOrigin = null;
[MonoPInvokeCallback(typeof(xrPollEventDelegate))]
private static XrResult xrPollEventInterceptor(XrInstance instance, ref XrEventDataBuffer eventData)
{
Profiler.BeginSample("ViveInterceptors:WaitFrame");
XrResult result = XrResult.XR_SUCCESS;
if (xrPollEventOrigin != null)
{
result = xrPollEventOrigin(instance, ref eventData);
if (result == XrResult.XR_SUCCESS)
{
sb.Clear().Append("xrPollEventInterceptor() xrPollEvent ").Append(eventData.type); DEBUG(sb);
switch(eventData.type)
{
case XrStructureType.XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_RATE_CHANGED_HTC:
if (XrEventDataPassthroughConfigurationImageRateChangedHTC.Get(eventData, out XrEventDataPassthroughConfigurationImageRateChangedHTC eventDataPassthroughConfigurationImageRate))
{
fromImageRate = eventDataPassthroughConfigurationImageRate.fromImageRate;
toImageRate = eventDataPassthroughConfigurationImageRate.toImageRate;
sb.Clear().Append("xrPollEventInterceptor() XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_RATE_CHANGED_HTC")
.Append(", fromImageRate.srcImageRate: ").Append(fromImageRate.srcImageRate)
.Append(", fromImageRatesrc.dstImageRate: ").Append(fromImageRate.dstImageRate)
.Append(", toImageRate.srcImageRate: ").Append(toImageRate.srcImageRate)
.Append(", toImageRate.dstImageRate: ").Append(toImageRate.dstImageRate);
DEBUG(sb);
VivePassthroughImageRateChanged.Send(fromImageRate.srcImageRate, fromImageRate.dstImageRate, toImageRate.srcImageRate, toImageRate.dstImageRate);
}
break;
case XrStructureType.XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_QUALITY_CHANGED_HTC:
if (XrEventDataPassthroughConfigurationImageQualityChangedHTC.Get(eventData, out XrEventDataPassthroughConfigurationImageQualityChangedHTC eventDataPassthroughConfigurationImageQuality))
{
fromImageQuality = eventDataPassthroughConfigurationImageQuality.fromImageQuality;
toImageQuality = eventDataPassthroughConfigurationImageQuality.toImageQuality;
sb.Clear().Append("xrPollEventInterceptor() XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_QUALITY_CHANGED_HTC")
.Append(", fromImageQuality: ").Append(fromImageQuality.scale)
.Append(", toImageQuality: ").Append(toImageQuality.scale);
DEBUG(sb);
VivePassthroughImageQualityChanged.Send(fromImageQuality.scale, toImageQuality.scale);
}
break;
case XrStructureType.XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB:
if(XrEventDataDisplayRefreshRateChangedFB.Get(eventData, out XrEventDataDisplayRefreshRateChangedFB eventDataDisplayRefreshRate))
{
fromDisplayRefreshRate = eventDataDisplayRefreshRate.fromDisplayRefreshRate;
toDisplayRefreshRate = eventDataDisplayRefreshRate.toDisplayRefreshRate;
sb.Clear().Append("xrPollEventInterceptor() XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB")
.Append(", fromDisplayRefreshRate: ").Append(fromDisplayRefreshRate)
.Append(", toDisplayRefreshRate: ").Append(toDisplayRefreshRate);
DEBUG(sb);
ViveDisplayRefreshRateChanged.Send(fromDisplayRefreshRate, toDisplayRefreshRate);
}
break;
case XrStructureType.XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
if (XrEventDataSessionStateChanged.Get(eventData, out XrEventDataSessionStateChanged eventDataSession))
{
switch(eventDataSession.state)
{
case XrSessionState.XR_SESSION_STATE_READY:
isUserPresent = true;
break;
case XrSessionState.XR_SESSION_STATE_STOPPING:
isUserPresent = false;
break;
default:
break;
}
sb.Clear().Append("xrPollEventInterceptor() XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED")
.Append(", session: ").Append(eventDataSession.session)
.Append(", state: ").Append(eventDataSession.state)
.Append(", isUserPresent: ").Append(isUserPresent);
DEBUG(sb);
}
break;
case XrStructureType.XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT:
if (XrEventDataUserPresenceChangedEXT.Get(eventData, out XrEventDataUserPresenceChangedEXT eventDataUserPresence))
{
isUserPresent = eventDataUserPresence.isUserPresent;
sb.Clear().Append("xrPollEventInterceptor() XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT")
.Append(", session: ").Append(eventDataUserPresence.session)
.Append(", isUserPresent: ").Append(isUserPresent);
DEBUG(sb);
}
break;
default:
break;
}
}
//sb.Clear().Append("xrPollEventInterceptor() xrPollEvent result: ").Append(result).Append(", isUserPresent: ").Append(isUserPresent); DEBUG(sb);
}
Profiler.EndSample();
return result;
}
private static readonly xrPollEventDelegate xrPollEvent = new xrPollEventDelegate(xrPollEventInterceptor);
private static readonly IntPtr xrPollEventPtr = Marshal.GetFunctionPointerForDelegate(xrPollEvent);
#endregion
private static bool isUserPresent = true;
public bool IsUserPresent() { return isUserPresent; }
private static float fromDisplayRefreshRate, toDisplayRefreshRate;
public float FromDisplayRefreshRate() { return fromDisplayRefreshRate; }
public float ToDisplayRefreshRate() { return toDisplayRefreshRate; }
private static XrPassthroughConfigurationImageRateHTC fromImageRate, toImageRate;
private static XrPassthroughConfigurationImageQualityHTC fromImageQuality, toImageQuality;
}
}

View File

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

View File

@@ -0,0 +1,89 @@
// Copyright HTC Corporation All Rights Reserved.
using System.Runtime.InteropServices;
using System;
using AOT;
using UnityEngine.Profiling;
namespace VIVE.OpenXR
{
partial class ViveInterceptors
{
public struct XrCompositionLayerBaseHeader
{
public XrStructureType type; // This base structure itself has no associated XrStructureType value.
public System.IntPtr next;
public XrCompositionLayerFlags layerFlags;
public XrSpace space;
}
public struct XrFrameEndInfo
{
public XrStructureType type;
public System.IntPtr next;
public XrTime displayTime;
public XrEnvironmentBlendMode environmentBlendMode;
public uint layerCount;
public IntPtr layers; // XrCompositionLayerBaseHeader IntPtr array
}
public delegate XrResult DelegateXrEndFrame(XrSession session, ref XrFrameEndInfo frameEndInfo);
private static readonly DelegateXrEndFrame xrEndFrameInterceptorHandle = new DelegateXrEndFrame(XrEndFrameInterceptor);
private static readonly IntPtr xrEndFrameInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrEndFrameInterceptorHandle);
static DelegateXrEndFrame XrEndFrameOriginal = null;
[MonoPInvokeCallback(typeof(DelegateXrEndFrame))]
private static XrResult XrEndFrameInterceptor(XrSession session, ref XrFrameEndInfo frameEndInfo)
{
// instance must not null
//if (instance == null)
// return XrEndFrameOriginal(session, ref frameEndInfo);
Profiler.BeginSample("VI:EndFrame");
XrResult result = XrResult.XR_SUCCESS;
if (instance.BeforeOriginalEndFrame != null &&
!instance.BeforeOriginalEndFrame(session, ref frameEndInfo, ref result))
{
Profiler.EndSample();
return result;
}
result = XrEndFrameOriginal(session, ref frameEndInfo);
instance.AfterOriginalEndFrame?.Invoke(session, ref frameEndInfo, ref result);
Profiler.EndSample();
return result;
}
/// <summary>
/// If you return false, the original function will not be called.
/// </summary>
/// <param name="session"></param>
/// <param name="frameEndInfo"></param>
/// <param name="result"></param>
/// <returns></returns>
public delegate bool DelegateXrEndFrameInterceptor(XrSession session, ref XrFrameEndInfo frameEndInfo, ref XrResult result);
/// <summary>
/// Use this to intercept the original function. This will be called before the original function.
/// </summary>
public DelegateXrEndFrameInterceptor BeforeOriginalEndFrame;
/// <summary>
/// Use this to intercept the original function. This will be called after the original function.
/// </summary>
public DelegateXrEndFrameInterceptor AfterOriginalEndFrame;
#if PERFORMANCE_TEST
public delegate XrResult DelegateXrLocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location);
private static readonly DelegateXrLocateSpace xrLocateSpaceInterceptorHandle = new DelegateXrLocateSpace(XrLocateSpaceInterceptor);
private static readonly IntPtr xrLocateSpaceInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrLocateSpaceInterceptorHandle);
static DelegateXrLocateSpace XrLocateSpaceOriginal = null;
[MonoPInvokeCallback(typeof(DelegateXrLocateSpace))]
public static XrResult XrLocateSpaceInterceptor(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location)
{
Profiler.BeginSample("VI:LocateSpace");
var ret = XrLocateSpaceOriginal(space, baseSpace, time, ref location);
Profiler.EndSample();
return ret;
}
#endif
}
}

View File

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

View File

@@ -0,0 +1,108 @@
// Copyright HTC Corporation All Rights Reserved.
using System.Runtime.InteropServices;
using System;
using UnityEngine;
using AOT;
using UnityEngine.Profiling;
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;
}
bool isWaitFrameIntercepted = false;
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)
{
// instance must not null
//if (instance == null)
// return XrWaitFrameOriginal(session, ref frameWaitInfo, ref frameState);
Profiler.BeginSample("VI:WaitFrame");
instance.isWaitFrameIntercepted = true;
XrResult result = XrResult.XR_SUCCESS;
if (instance.BeforeOriginalWaitFrame != null &&
!instance.BeforeOriginalWaitFrame(session, ref frameWaitInfo, ref frameState, ref result))
{
Profiler.EndSample();
return result;
}
var ret = XrWaitFrameOriginal(session, ref frameWaitInfo, ref frameState);
instance.AfterOriginalWaitFrame?.Invoke(session, ref frameWaitInfo, ref frameState, ref result);
currentFrameState = frameState;
Profiler.EndSample();
return result;
}
static XrFrameState currentFrameState = new XrFrameState() { predictedDisplayTime = 0 };
/// <summary>
/// Get the waitframe's result: XrFrameState. This result used in update is not matching the current frame. Use it after onBeforeRender.
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public XrFrameState GetCurrentFrameState()
{
if (!isWaitFrameIntercepted) throw new Exception("ViveInterceptors is not intercepted");
return currentFrameState;
}
/// <summary>
/// Must request xrWaitFrame before calling this function. This result used in update is not matching the current frame. Use it after onBeforeRender.
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public XrTime GetPredictTime()
{
if (!isWaitFrameIntercepted) throw new Exception("ViveInterceptors is not intercepted");
//Debug.Log($"{TAG}: XrWaitFrameInterceptor(predictedDisplayTime={currentFrameState.predictedDisplayTime}");
if (currentFrameState.predictedDisplayTime == 0)
return new XrTime((long)(1000000L * (Time.unscaledTimeAsDouble + 0.011f)));
else
return currentFrameState.predictedDisplayTime;
}
/// <summary>
/// Register WaitFrame event
/// </summary>
/// <param name="session"></param>
/// <param name="frameWaitInfo"></param>
/// <param name="frameState"></param>
/// <param name="result"></param>
/// <returns></returns>
public delegate bool DelegateXrWaitFrameInterceptor(XrSession session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState, ref XrResult result);
/// <summary>
/// Use this to intercept the original function. This will be called before the original function.
/// </summary>
public DelegateXrWaitFrameInterceptor BeforeOriginalWaitFrame;
/// <summary>
/// Use this to intercept the original function. This will be called after the original function.
/// </summary>
public DelegateXrWaitFrameInterceptor AfterOriginalWaitFrame;
#endregion XRWaitFrame
}
}

View File

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

View File

@@ -0,0 +1,329 @@
using AOT;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace VIVE.OpenXR.Common.RenderThread
{
#region syncObject
public class Message
{
public bool isFree = true;
}
/// <summary>
/// MessagePool class manages a pool of message objects for reuse. You can enter any kind of message object.
/// However when obtain, the message object will not able to cast to the type you want.
/// You should only use one kind of message. Not mix different kind of message.
/// </summary>
public class MessagePool
{
// pool member is used to store message objects in a list.
// Note that the size of this list will dynamically adjust as needed but will not automatically shrink.
private readonly List<Message> pool = new List<Message>(2) { };
private int index = 0;
public MessagePool() { }
// Next method calculates the next index value for cycling through message objects in the pool.
private int Next(int value)
{
if (++value >= pool.Count)
value = 0;
return value;
}
// Obtain method retrieves a message object from the pool.
// Ensure proper state setup for the message after retrieval and call Release() to the message after use.
public T Obtain<T>() where T : Message, new()
{
int c = pool.Count;
int i = index;
for (int j = 0; j < c; i++, j++)
{
if (i >= c)
i = 0;
if (pool[i].isFree)
{
//Debug.LogError("Obtain idx=" + i);
index = i;
return (T)pool[i];
}
}
index = Next(i);
var newItem = new T()
{
isFree = true
};
pool.Insert(index, newItem);
Debug.Log("RT.MessagePool.Obtain() pool count=" + pool.Count);
return newItem;
}
// Lock method marks a message as "in use" to prevent other code from reusing it.
// This is already called to the message obtained from the pool.
public static void Lock(Message msg)
{
msg.isFree = false;
}
/// <summary>
/// Release method marks a message as "free" so that other code can reuse it.
/// You can use it in RenderThread. It will not trigger the GC event.
/// </summary>
/// <param name="msg"></param>
public static void Release(Message msg)
{
msg.isFree = true;
}
}
/// <summary>
/// PreAllocatedQueue class is a message queue based on MessagePool for preallocating message objects.
/// Its main functionality is to add message objects to the queue and retrieve them from the queue.
/// Messages should be enqueued in GameThread and dequeued in RenderThread.
/// In render thread, dequeue will not trigger the GC event. Because the queue is preallocated.
/// The 'lock' expression is not used for list's size change. Because lock should be avoid used in RenderThread.
/// Set the queueSize as the double count of message you want to pass to render thread in one frame, and the
/// list will never change size during runtime. Therefore we don't need to use 'lock' to protect the list.
/// </summary>
public class PreAllocatedQueue : MessagePool
{
// list member is used to store preallocated message objects in a list.
// Note that the size of this list is set during initialization and does not dynamically adjust.
private List<Message> list = new List<Message>();
private int queueBegin = 0;
private int queueEnd = 0;
/// <summary>
/// The queueSize should be the double count of message you want to pass to render thread in one frame.
/// </summary>
/// <param name="queueSize"></param>
public PreAllocatedQueue(int queueSize = 2) : base() {
for (int i = 0; i < queueSize; i++)
{
list.Add(null);
}
}
private int Next(int value)
{
if (++value >= list.Count)
value = 0;
return value;
}
/// <summary>
/// Enqueue method adds a message object to the queue.
/// If the queue is full, the new message is added to the end of the list.
///
/// This function is designed to use the message object obtained from the MessagePool.
/// Ensure only one type of message object is used in the queue.
///
/// Enqueue will increase the queue size if the queue is full. This may trigger GC.Alloc.
/// This function should be used in GameThread.
/// </summary>
/// <param name="msg"></param>
public void Enqueue(Message msg)
{
Lock(msg);
queueEnd = Next(queueEnd);
// If the queue is full, add the message to the end of the list. Should not let it happen.
// Use larger queue size to avoid this issue.
// If you see the error log here, you should increase the queue size in your design.
if (queueEnd == queueBegin)
{
// Should let Insert and queueBegin be atomic. No lock protection here.
list.Insert(queueEnd, msg);
queueBegin++;
Debug.LogError("RT.MessagePool.Enqueue() list count=" + list.Count);
}
else
{
list[queueEnd] = msg;
}
}
/// <summary>
/// Dequeue method retrieves a message object from the queue.
/// This method returns the first message object in the queue and removes it from the queue.
/// This function will not trigger the GC event. Free to use in RenderThread.
/// After use the Message, call Release() to the message.
/// </summary>
/// <returns></returns>
public Message Dequeue()
{
// No lock protection here. If list is not change size, it is safe.
// However if list changed size, it is safe in most case.
queueBegin = Next(queueBegin);
return list[queueBegin];
}
}
/// <summary>
/// RenderThreadTask class is used to execute specified tasks on the rendering thread.
/// You don't need to develop a native function to run your task on the rendering thread.
/// And you don't need to design how to pass data to render thread.
/// This class can be run in Unity Editor since Unity 2021. Test your code in Unity Editor can save your time.
///
/// You should only create RenderThreadTask as static readonly. Do not create RenderThreadTask in dynamic.
///
/// You should not run Unity.Engine code in RenderThread. It will cause the Unity.Engine to hang.
/// Any exception will not be caught and shown in RenderThread.
/// You should print your error message out to clearify your issue.
///
/// The 'lock' expression is not used here. Because I believe the lock is not necessary in this case.
/// And the lock will cause the performance issue. All the design here help you not to use 'lock'.
/// </summary>
public class RenderThreadTask
{
private static IntPtr GetFunctionPointerForDelegate(Delegate del)
{
return System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(del);
}
public delegate void RenderEventDelegate(int e);
private static readonly RenderEventDelegate handle = new RenderEventDelegate(RunSyncObjectInRenderThread);
private static readonly IntPtr handlePtr = GetFunctionPointerForDelegate(handle);
public delegate void Receiver(PreAllocatedQueue dataQueue);
// CommandList is used to store all RenderThreadTask objects.
// Do not create RenderThreadTask object in dynamic. It will cause the CommandList to increase infinitly.
private static List<RenderThreadTask> CommandList = new List<RenderThreadTask>();
private PreAllocatedQueue queue;
public PreAllocatedQueue Queue { get { return queue; } }
private readonly Receiver receiver;
private readonly int id;
/// <summary>
/// Input the receiver as render thread callback. The receiver will be executed in render thread.
/// queueSize should be the double count of message you want to pass to render thread in one frame.
/// </summary>
/// <param name="render">The callback in render thread.</param>
/// <param name="queueSize">If issue this event once in a frame, set queueSize as 2.</param>
/// <exception cref="ArgumentNullException"></exception>
public RenderThreadTask(Receiver render, int queueSize = 2)
{
queue = new PreAllocatedQueue(queueSize);
receiver = render;
if (receiver == null)
throw new ArgumentNullException("receiver should not be null");
CommandList.Add(this);
id = CommandList.IndexOf(this);
}
~RenderThreadTask()
{
try { CommandList.RemoveAt(id); } finally { }
}
void IssuePluginEvent(IntPtr callback, int eventID)
{
// Older version will hang after run script in render thread.
GL.IssuePluginEvent(callback, eventID);
return;
}
void IssuePluginEvent(CommandBuffer cmdBuf, IntPtr callback, int eventID)
{
cmdBuf.IssuePluginEvent(callback, eventID);
return;
}
/// <summary>
/// IssueEvent method submits this task's receiver, which is set in constructor, to be executed on the rendering thread.
/// </summary>
public void IssueEvent()
{
// Let the render thread run the RunSyncObjectInRenderThread(id)
IssuePluginEvent(handlePtr, id);
}
public void IssueInCommandBuffer(CommandBuffer cmdBuf)
{
// Let the render thread run the RunSyncObjectInRenderThread(id)
IssuePluginEvent(cmdBuf, handlePtr, id);
}
// Called by RunSyncObjectInRenderThread()
private void Receive()
{
receiver(queue);
}
// RunSyncObjectInRenderThread method is a static method used to execute a specified task on the rendering thread.
// This method is invoked by Unity's rendering event mechanism and does not need to be called directly by developers.
[MonoPInvokeCallback(typeof(RenderEventDelegate))]
private static void RunSyncObjectInRenderThread(int id)
{
CommandList[id].Receive();
}
}
#endregion
#region sample
// Not to compile this sample into your application. Just for reference. You can run this sample in Unity Editor and it will work.
#if UNITY_EDITOR
public class ViveRenderThreadTaskSample : MonoBehaviour
{
// Create your own message class.
internal class SampleMessage : Message
{
public int dataPassedToRenderThread;
}
// Use static readonly to create RenderThreadTask. Keep internal to avoid miss use by other developers.
internal static readonly RenderThreadTask sampleRenderThreadTask1 = new RenderThreadTask(SampleReceiver1);
// Different task use different RenderThreadTask and different recevier.
internal static readonly RenderThreadTask sampleRenderThreadTask2 = new RenderThreadTask(SampleReceiver2);
private static void SampleReceiver1(PreAllocatedQueue dataQueue)
{
var msg = dataQueue.Dequeue() as SampleMessage;
// no need to check msg if it is null because your design should avoid it.
// Keep data before release. Use local variable to keep data and release msg early. Should not keep the msg instance itself.
var data = msg.dataPassedToRenderThread;
// Make sure release the msg if finished. Other wise the memory will keep increasing when Obtain.
MessagePool.Release(msg);
Debug.Log("Task1, the data passed to render thread: " + data);
}
private static void SampleReceiver2(PreAllocatedQueue dataQueue)
{
var msg = dataQueue.Dequeue() as SampleMessage;
var data = msg.dataPassedToRenderThread;
MessagePool.Release(msg);
Debug.Log("Task2, the data passed to render thread: " + data);
}
// Send a message to the render thread every frame.
private void Update()
{
// Make sure only one kind of message object is used in the queue.
var msg = sampleRenderThreadTask1.Queue.Obtain<SampleMessage>();
msg.dataPassedToRenderThread = 123;
sampleRenderThreadTask1.Queue.Enqueue(msg);
sampleRenderThreadTask1.IssueEvent();
}
// Send a message to render thread when something clicked. Make sure only one click in one frame because the queue size is only two.
public void OnClicked()
{
// Reuse the same message type is ok.
var msg = sampleRenderThreadTask2.Queue.Obtain<SampleMessage>();
msg.dataPassedToRenderThread = 234;
sampleRenderThreadTask2.Queue.Enqueue(msg);
sampleRenderThreadTask2.IssueEvent();
}
}
#endif
#endregion
}

View File

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

View File

@@ -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 HTCs 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
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 33a39a24d09f3cf48ae13735ee2209f6
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
preprocessorOverride: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -96,9 +96,29 @@ namespace VIVE.OpenXR.CompositionLayer
[SerializeField] [SerializeField]
public bool isDynamicLayer = false; public bool isDynamicLayer = false;
[SerializeField]
public bool isExternalSurface = false;
[SerializeField]
public bool isCustomRects = false;
[SerializeField]
public CustomRectsType customRects = CustomRectsType.TopDown;
[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;
@@ -108,9 +128,13 @@ namespace VIVE.OpenXR.CompositionLayer
[SerializeField] [SerializeField]
public bool isProtectedSurface = false; public bool isProtectedSurface = false;
[SerializeField]
public Texture texture = null; public Texture texture = null;
private Texture m_TextureLeft => texture;
public Texture textureLeft { get { return m_TextureLeft; } }
public Texture textureRight = null;
[SerializeField] [SerializeField]
private uint renderPriority = 0; private uint renderPriority = 0;
public uint GetRenderPriority() { return renderPriority; } public uint GetRenderPriority() { return renderPriority; }
@@ -136,7 +160,7 @@ namespace VIVE.OpenXR.CompositionLayer
private MeshRenderer generatedFallbackMeshRenderer = null; private MeshRenderer generatedFallbackMeshRenderer = null;
private MeshFilter generatedFallbackMeshFilter = null; private MeshFilter generatedFallbackMeshFilter = null;
private LayerTextures layerTextures; private LayerTextures[] layerTextures = new LayerTextures[] {null, null};
private Material texture2DBlitMaterial; private Material texture2DBlitMaterial;
private GameObject compositionLayerPlaceholderPrefabGO = null; private GameObject compositionLayerPlaceholderPrefabGO = null;
@@ -151,10 +175,12 @@ namespace VIVE.OpenXR.CompositionLayer
private float previousCylinderArcLength = 1f; private float previousCylinderArcLength = 1f;
private float previousCylinderRadius = 1f; private float previousCylinderRadius = 1f;
private float previousAngleOfArc = 180f; private float previousAngleOfArc = 180f;
private Texture previousTexture = null; private Texture previousTextureLeft = null;
private Texture previousTextureRight = null;
private bool previousIsDynamicLayer = false; private bool previousIsDynamicLayer = false;
private int layerID; //For native private int layerID; //For native
private int layerIDRight; //For native
private bool isHeadLock = false; private bool isHeadLock = false;
private bool InitStatus = false; private bool InitStatus = false;
private bool isInitializationComplete = false; private bool isInitializationComplete = false;
@@ -184,16 +210,93 @@ namespace VIVE.OpenXR.CompositionLayer
return true; return true;
} }
if (texture == null) 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[0] = new LayerTextures(imageCount);
InitStatus = true;
}
if (textureRight != null && textureLeft != textureRight) {
layerIDRight = compositionLayerFeature.CompositionLayer_Init(externalSurfaceWidth, externalSurfaceHeight, graphicsAPI, isDynamicLayer, isProtectedSurface, out imageCount, true);
if (layerIDRight != 0)
{
DEBUG("Init completed, ID right: " + layerIDRight);
layerTextures[1] = new LayerTextures(imageCount);
}
}
else if (isCustomRects) {
layerIDRight = compositionLayerFeature.CompositionLayer_Init(externalSurfaceWidth, externalSurfaceHeight, graphicsAPI, isDynamicLayer, isProtectedSurface, out imageCount, true);
if (layerIDRight != 0)
{
DEBUG("Init completed, ID right: " + layerIDRight);
layerTextures[1] = new LayerTextures(imageCount);
}
}
taskQueue.Release(task);
}
});
CompositionLayerRenderThreadTask.IssueObtainSwapchainEvent(SetupExternalAndroidSurfaceSyncObjects);
texture = new Texture2D((int)externalSurfaceWidth, (int)externalSurfaceHeight, TextureFormat.RGBA32, false, isLinear);
textureRight = new Texture2D((int)externalSurfaceWidth, (int)externalSurfaceHeight, TextureFormat.RGBA32, false, isLinear);
DEBUG("CompositionLayerInit Ext Surf");
return true;
}
if (textureLeft == null)
{ {
ERROR("CompositionLayerInit: Source Texture not found, abort init."); ERROR("CompositionLayerInit: Source Texture not found, abort init.");
return false; return false;
} }
if (textureLeft != null && textureRight == null)
{
DEBUG("CompositionLayerInit: Using Left Texture as Right Texture.");
textureRight = textureLeft;
}
DEBUG("CompositionLayerInit"); DEBUG("CompositionLayerInit");
uint textureWidth = (uint)texture.width; uint textureWidth = (uint)textureLeft.width;
uint textureHeight = (uint)texture.height; uint textureHeight = (uint)textureLeft.height;
DEBUG("Init : textureWidth = " + textureWidth + " textureHeight = " + textureHeight);
CompositionLayerRenderThreadSyncObject ObtainLayerSwapchainSyncObject = new CompositionLayerRenderThreadSyncObject( CompositionLayerRenderThreadSyncObject ObtainLayerSwapchainSyncObject = new CompositionLayerRenderThreadSyncObject(
(taskQueue) => (taskQueue) =>
@@ -227,9 +330,28 @@ namespace VIVE.OpenXR.CompositionLayer
if (layerID != 0) if (layerID != 0)
{ {
DEBUG("Init completed, ID: " + layerID + ", Image Count: " + imageCount); DEBUG("Init completed, ID: " + layerID + ", Image Count: " + imageCount);
layerTextures = new LayerTextures(imageCount); layerTextures[0] = new LayerTextures(imageCount);
InitStatus = true; InitStatus = true;
} }
if (textureRight != null && textureLeft != textureRight) {
layerIDRight = compositionLayerFeature.CompositionLayer_Init(textureWidth, textureHeight, graphicsAPI, isDynamicLayer, isProtectedSurface, out imageCount);
if (layerIDRight != 0)
{
DEBUG("Init completed, ID Right: " + layerIDRight + ", Image Count: " + imageCount);
layerTextures[1] = new LayerTextures(imageCount);
}
}
else if (isCustomRects)
{
layerIDRight = compositionLayerFeature.CompositionLayer_Init(textureWidth, textureHeight, graphicsAPI, isDynamicLayer, isProtectedSurface, out imageCount);
if (layerIDRight != 0)
{
DEBUG("Init completed, ID Right: " + layerIDRight + ", Image Count: " + imageCount);
layerTextures[1] = new LayerTextures(imageCount);
}
}
taskQueue.Release(task); taskQueue.Release(task);
} }
@@ -244,83 +366,125 @@ namespace VIVE.OpenXR.CompositionLayer
previousCylinderArcLength = m_CylinderArcLength; previousCylinderArcLength = m_CylinderArcLength;
previousCylinderRadius = m_CylinderRadius; previousCylinderRadius = m_CylinderRadius;
previousAngleOfArc = m_CylinderAngleOfArc; previousAngleOfArc = m_CylinderAngleOfArc;
previousTexture = texture; previousTextureLeft = textureLeft;
previousTextureRight = textureRight;
previousIsDynamicLayer = isDynamicLayer; previousIsDynamicLayer = isDynamicLayer;
return true; return true;
} }
private bool textureAcquired = false; private bool[] textureAcquired = new bool[] {false, false};
private bool textureAcquiredOnce = false; private bool[] textureAcquiredOnce = new bool[] {false, false};
XrOffset2Di offset = new XrOffset2Di(); XrOffset2Di offset = new XrOffset2Di();
XrExtent2Di extent = new XrExtent2Di(); XrExtent2Di extent = new XrExtent2Di();
XrRect2Di rect = new XrRect2Di(); XrRect2Di rect = new XrRect2Di();
private bool SetLayerTexture()
private bool SetLayerTexture(int eyeid)
{ {
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;
if (isCustomRects && customRects == CustomRectsType.TopDown)
{
extent.height = (int)-externalSurfaceHeight/2;
if (eyeid == 0)
offset.y = (int)(externalSurfaceHeight-externalSurfaceHeight/2);
}
else if (isCustomRects && customRects == CustomRectsType.LeftRight)
{
extent.width = (int)externalSurfaceWidth/2;
if (eyeid != 0)
offset.x = extent.width;
}
rect.offset = offset;
rect.extent = extent;
layerTextures[eyeid].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())
{ {
//Destroy queues //Destroy queues
DEBUG("SetLayerTexture: Texture params changed, need to re-init queues. layerID: " + layerID); DEBUG("SetLayerTexture: Texture params changed, need to re-init queues. layerID: " + ((eyeid ==0) ? layerID : layerIDRight));
DestroyCompositionLayer(); if (layerID != 0)
{
DestroyCompositionLayer(0);
layerID = 0;
}
if (layerIDRight != 0)
{
DestroyCompositionLayer(1);
layerIDRight = 0;
}
reinitializationNeeded = true; reinitializationNeeded = true;
return false; return false;
} }
} }
else else
{ {
ERROR("SetLayerTexture: No texture found. layerID: " + layerID); ERROR("SetLayerTexture: No texture found. layerID: " + ((eyeid ==0) ? layerID : layerIDRight));
return false; return false;
} }
if (isDynamicLayer || (!isDynamicLayer && !textureAcquiredOnce)) if (isDynamicLayer || (!isDynamicLayer && !textureAcquiredOnce[eyeid]))
{ {
//Get available texture id //Get available texture id
compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>(); compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>();
uint currentImageIndex; uint currentImageIndex;
IntPtr newTextureID = compositionLayerFeature.CompositionLayer_GetTexture(layerID, out currentImageIndex); IntPtr newTextureID = compositionLayerFeature.CompositionLayer_GetTexture((eyeid ==0) ? layerID : layerIDRight, out currentImageIndex);
textureAcquired = true; textureAcquired[eyeid] = true;
textureAcquiredOnce = true; textureAcquiredOnce[eyeid] = true;
if (newTextureID == IntPtr.Zero) if (newTextureID == IntPtr.Zero)
{ {
ERROR("SetLayerTexture: Invalid Texture ID"); ERROR("SetLayerTexture: Invalid Texture ID");
if (compositionLayerFeature.CompositionLayer_ReleaseTexture(layerID)) if (compositionLayerFeature.CompositionLayer_ReleaseTexture((eyeid ==0) ? layerID : layerIDRight))
{ {
textureAcquired = false; textureAcquired[eyeid] = false;
} }
return false; return false;
} }
bool textureIDUpdated = false; bool textureIDUpdated = false;
layerTextures.currentAvailableTextureIndex = currentImageIndex; layerTextures[eyeid].currentAvailableTextureIndex = currentImageIndex;
IntPtr currentTextureID = layerTextures.GetCurrentAvailableTextureID(); IntPtr currentTextureID = layerTextures[eyeid].GetCurrentAvailableTextureID();
if (currentTextureID == IntPtr.Zero || currentTextureID != newTextureID) if (currentTextureID == IntPtr.Zero || currentTextureID != newTextureID)
{ {
DEBUG("SetLayerTexture: Update Texture ID. layerID: " + layerID); DEBUG("SetLayerTexture: Update Texture ID. layerID: " + ((eyeid ==0) ? layerID : layerIDRight));
layerTextures.SetCurrentAvailableTextureID(newTextureID); layerTextures[eyeid].SetCurrentAvailableTextureID(newTextureID);
textureIDUpdated = true; textureIDUpdated = true;
} }
if (layerTextures.GetCurrentAvailableTextureID() == IntPtr.Zero) if (layerTextures[eyeid].GetCurrentAvailableTextureID() == IntPtr.Zero)
{ {
ERROR("SetLayerTexture: Failed to get texture."); ERROR("SetLayerTexture: Failed to get texture.");
return false; return false;
} }
// Create external texture // Create external texture
if (layerTextures.GetCurrentAvailableExternalTexture() == null || textureIDUpdated) if (layerTextures[eyeid].GetCurrentAvailableExternalTexture() == null || textureIDUpdated)
{ {
DEBUG("SetLayerTexture: Create External Texture."); DEBUG("SetLayerTexture: Create External Texture.");
layerTextures.SetCurrentAvailableExternalTexture(Texture2D.CreateExternalTexture(texture.width, texture.height, TextureFormat.RGBA32, false, isLinear, layerTextures.GetCurrentAvailableTextureID())); layerTextures[eyeid].SetCurrentAvailableExternalTexture(Texture2D.CreateExternalTexture(texture.width, texture.height, TextureFormat.RGBA32, false, isLinear, layerTextures[eyeid].GetCurrentAvailableTextureID()));
} }
if (layerTextures.externalTextures[layerTextures.currentAvailableTextureIndex] == null) if (layerTextures[eyeid].externalTextures[layerTextures[eyeid].currentAvailableTextureIndex] == null)
{ {
ERROR("SetLayerTexture: Create External Texture Failed."); ERROR("SetLayerTexture: Create External Texture Failed.");
return false; return false;
@@ -329,28 +493,40 @@ namespace VIVE.OpenXR.CompositionLayer
//Set Texture Content //Set Texture Content
bool isContentSet = layerTextures.textureContentSet[layerTextures.currentAvailableTextureIndex]; bool isContentSet = layerTextures[eyeid].textureContentSet[layerTextures[eyeid].currentAvailableTextureIndex];
if (!isDynamicLayer && isContentSet) if (!isDynamicLayer && isContentSet)
{ {
return true; return true;
} }
int currentTextureWidth = layerTextures[eyeid].GetCurrentAvailableExternalTexture().width;
int currentTextureWidth = layerTextures.GetCurrentAvailableExternalTexture().width; int currentTextureHeight = layerTextures[eyeid].GetCurrentAvailableExternalTexture().height;
int currentTextureHeight = layerTextures.GetCurrentAvailableExternalTexture().height;
//Set Texture Layout //Set Texture Layout
offset.x = 0; offset.x = 0;
offset.y = 0; offset.y = 0;
extent.width = (int)currentTextureWidth; extent.width = (int)currentTextureWidth;
extent.height = (int)currentTextureHeight; extent.height = (int)currentTextureHeight;
if (isCustomRects && customRects == CustomRectsType.TopDown)
{
extent.height = (int)currentTextureHeight/2;
if (eyeid == 0)
offset.y = extent.height;
}
else if (isCustomRects && customRects == CustomRectsType.LeftRight)
{
extent.width = (int)currentTextureWidth/2;
if (eyeid != 0)
offset.x = extent.width;
}
rect.offset = offset; rect.offset = offset;
rect.extent = extent; rect.extent = extent;
layerTextures[eyeid].textureLayout = rect;
layerTextures.textureLayout = rect; RenderTexture srcTexture = ((eyeid == 0 || isCustomRects) ? textureLeft : textureRight) as RenderTexture;
//Blit and copy texture
RenderTexture srcTexture = texture as RenderTexture;
int msaaSamples = 1; int msaaSamples = 1;
if (srcTexture != null) if (srcTexture != null)
{ {
@@ -359,6 +535,8 @@ namespace VIVE.OpenXR.CompositionLayer
Material currentBlitMat = texture2DBlitMaterial; Material currentBlitMat = texture2DBlitMaterial;
DEBUG("RenderTextureDescriptor currentTextureWidth = " + currentTextureWidth + " currentTextureHeight = " + currentTextureHeight);
RenderTextureDescriptor rtDescriptor = new RenderTextureDescriptor(currentTextureWidth, currentTextureHeight, RenderTextureFormat.ARGB32, 0); RenderTextureDescriptor rtDescriptor = new RenderTextureDescriptor(currentTextureWidth, currentTextureHeight, RenderTextureFormat.ARGB32, 0);
rtDescriptor.msaaSamples = msaaSamples; rtDescriptor.msaaSamples = msaaSamples;
rtDescriptor.autoGenerateMips = false; rtDescriptor.autoGenerateMips = false;
@@ -372,8 +550,10 @@ namespace VIVE.OpenXR.CompositionLayer
} }
blitTempRT.DiscardContents(); blitTempRT.DiscardContents();
Texture dstTexture = layerTextures.GetCurrentAvailableExternalTexture(); Texture dstTexture = layerTextures[eyeid].GetCurrentAvailableExternalTexture();
Graphics.Blit(texture, blitTempRT, currentBlitMat);
Graphics.Blit((eyeid == 0) ? textureLeft : textureRight, blitTempRT, currentBlitMat);
Graphics.CopyTexture(blitTempRT, 0, 0, dstTexture, 0, 0); Graphics.CopyTexture(blitTempRT, 0, 0, dstTexture, 0, 0);
//DEBUG("Blit and CopyTexture complete."); //DEBUG("Blit and CopyTexture complete.");
@@ -388,12 +568,12 @@ namespace VIVE.OpenXR.CompositionLayer
return false; return false;
} }
layerTextures.textureContentSet[layerTextures.currentAvailableTextureIndex] = true; layerTextures[eyeid].textureContentSet[layerTextures[eyeid].currentAvailableTextureIndex] = true;
bool releaseTextureResult = compositionLayerFeature.CompositionLayer_ReleaseTexture(layerID); bool releaseTextureResult = compositionLayerFeature.CompositionLayer_ReleaseTexture((eyeid == 0) ? layerID : layerIDRight);
if (releaseTextureResult) if (releaseTextureResult)
{ {
textureAcquired = false; textureAcquired[eyeid] = false;
} }
return releaseTextureResult; return releaseTextureResult;
@@ -445,7 +625,7 @@ namespace VIVE.OpenXR.CompositionLayer
bool enabledColorScaleBiasInShader = false; bool enabledColorScaleBiasInShader = false;
XrCompositionLayerColorScaleBiasKHR CompositionLayerParamsColorScaleBias = new XrCompositionLayerColorScaleBiasKHR(); XrCompositionLayerColorScaleBiasKHR CompositionLayerParamsColorScaleBias = new XrCompositionLayerColorScaleBiasKHR();
private void SubmitCompositionLayer() //Call at onBeforeRender private void SubmitCompositionLayer(int eyeid, bool botheye) //Call at onBeforeRender
{ {
if (!isInitializationComplete && !isLayerReadyForSubmit) return; if (!isInitializationComplete && !isLayerReadyForSubmit) return;
compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>(); compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>();
@@ -457,7 +637,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,8 +662,13 @@ 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, (eyeid == 0) ? layerID : layerIDRight);
} }
else if (enabledColorScaleBiasInShader) //Disable if color scale bias is no longer active else if (enabledColorScaleBiasInShader) //Disable if color scale bias is no longer active
{ {
@@ -496,13 +681,13 @@ namespace VIVE.OpenXR.CompositionLayer
{ {
default: default:
case LayerShape.Quad: case LayerShape.Quad:
compositionLayerFeature.Submit_CompositionLayerQuad(AssignCompositionLayerParamsQuad(), (OpenXR.CompositionLayer.LayerType)layerType, compositionDepth, layerID); compositionLayerFeature.Submit_CompositionLayerQuad(AssignCompositionLayerParamsQuad(eyeid, botheye), (OpenXR.CompositionLayer.LayerType)layerType, compositionDepth, (eyeid == 0) ? layerID : layerIDRight);
break; break;
case LayerShape.Cylinder: case LayerShape.Cylinder:
ViveCompositionLayerCylinder compositionLayerCylinderFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayerCylinder>(); ViveCompositionLayerCylinder compositionLayerCylinderFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayerCylinder>();
if (compositionLayerCylinderFeature != null && compositionLayerCylinderFeature.CylinderExtensionEnabled) if (compositionLayerCylinderFeature != null && compositionLayerCylinderFeature.CylinderExtensionEnabled)
{ {
compositionLayerCylinderFeature.Submit_CompositionLayerCylinder(AssignCompositionLayerParamsCylinder(), (OpenXR.CompositionLayer.LayerType)layerType, compositionDepth, layerID); compositionLayerCylinderFeature.Submit_CompositionLayerCylinder(AssignCompositionLayerParamsCylinder(eyeid, botheye), (OpenXR.CompositionLayer.LayerType)layerType, compositionDepth, (eyeid == 0) ? layerID : layerIDRight);
} }
break; break;
} }
@@ -514,22 +699,22 @@ namespace VIVE.OpenXR.CompositionLayer
public delegate void OnDestroyCompositionLayer(); public delegate void OnDestroyCompositionLayer();
public event OnDestroyCompositionLayer OnDestroyCompositionLayerDelegate = null; public event OnDestroyCompositionLayer OnDestroyCompositionLayerDelegate = null;
private void DestroyCompositionLayer() private void DestroyCompositionLayer(int eyeid)
{ {
if (!isInitializationComplete || layerTextures == null) if (layerTextures[eyeid] == null)
{ {
DEBUG("DestroyCompositionLayer: Layer already destroyed/not initialized."); DEBUG("DestroyCompositionLayer: Layer already destroyed/not initialized.");
return; return;
} }
DEBUG("DestroyCompositionLayer");
compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>(); compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>();
if (textureAcquired) if (textureAcquired[eyeid])
{ {
DEBUG("DestroyCompositionLayer: textureAcquired, releasing."); DEBUG("DestroyCompositionLayer: textureAcquired, releasing.");
textureAcquired = !compositionLayerFeature.CompositionLayer_ReleaseTexture(layerID); textureAcquired[eyeid] = !compositionLayerFeature.CompositionLayer_ReleaseTexture((eyeid == 0) ? layerID : layerIDRight);
} }
CompositionLayerRenderThreadSyncObject DestroyLayerSwapchainSyncObject = new CompositionLayerRenderThreadSyncObject( CompositionLayerRenderThreadSyncObject DestroyLayerSwapchainSyncObject = new CompositionLayerRenderThreadSyncObject(
@@ -544,26 +729,26 @@ namespace VIVE.OpenXR.CompositionLayer
if (!compositionLayerFeature.CompositionLayer_Destroy(task.layerID)) if (!compositionLayerFeature.CompositionLayer_Destroy(task.layerID))
{ {
ERROR("estroyCompositionLayer: CompositionLayer_Destroy failed."); ERROR("DestroyCompositionLayer: CompositionLayer_Destroy failed : " + task.layerID);
} }
taskQueue.Release(task); taskQueue.Release(task);
} }
}); });
CompositionLayerRenderThreadTask.IssueDestroySwapchainEvent(DestroyLayerSwapchainSyncObject, layerID); CompositionLayerRenderThreadTask.IssueDestroySwapchainEvent(DestroyLayerSwapchainSyncObject, (eyeid == 0) ? layerID : layerIDRight);
InitStatus = false; InitStatus = false;
isLayerReadyForSubmit = false; isLayerReadyForSubmit = false;
isInitializationComplete = false; isInitializationComplete = false;
textureAcquiredOnce = false; textureAcquiredOnce[eyeid] = false;
foreach (Texture externalTexture in layerTextures.externalTextures) foreach (Texture externalTexture in layerTextures[eyeid].externalTextures)
{ {
DEBUG("DestroyCompositionLayer: External textures"); DEBUG("DestroyCompositionLayer: External textures");
if (externalTexture != null) Destroy(externalTexture); if (externalTexture != null) Destroy(externalTexture);
} }
layerTextures = null; layerTextures[eyeid] = null;
if (generatedFallbackMeshFilter != null && generatedFallbackMeshFilter.mesh != null) if (generatedFallbackMeshFilter != null && generatedFallbackMeshFilter.mesh != null)
{ {
@@ -609,13 +794,18 @@ namespace VIVE.OpenXR.CompositionLayer
private List<XRInputSubsystem> inputSubsystems = new List<XRInputSubsystem>(); private List<XRInputSubsystem> inputSubsystems = new List<XRInputSubsystem>();
XrCompositionLayerQuad CompositionLayerParamsQuad = new XrCompositionLayerQuad(); XrCompositionLayerQuad CompositionLayerParamsQuad = new XrCompositionLayerQuad();
XrExtent2Df quadSize = new XrExtent2Df(); XrExtent2Df quadSize = new XrExtent2Df();
private XrCompositionLayerQuad AssignCompositionLayerParamsQuad() private XrCompositionLayerQuad AssignCompositionLayerParamsQuad(int eyeid, bool botheye)
{ {
compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>(); compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>();
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:
@@ -630,7 +820,14 @@ namespace VIVE.OpenXR.CompositionLayer
break; break;
} }
CompositionLayerParamsQuad.subImage.imageRect = layerTextures.textureLayout; if (!botheye) {
if (eyeid == 0)
CompositionLayerParamsQuad.eyeVisibility = XrEyeVisibility.XR_EYE_VISIBILITY_LEFT;
else
CompositionLayerParamsQuad.eyeVisibility = XrEyeVisibility.XR_EYE_VISIBILITY_RIGHT;
}
CompositionLayerParamsQuad.subImage.imageRect = layerTextures[eyeid].textureLayout;
CompositionLayerParamsQuad.subImage.imageArrayIndex = 0; CompositionLayerParamsQuad.subImage.imageArrayIndex = 0;
GetCompositionLayerPose(ref CompositionLayerParamsQuad.pose); //Update isHeadLock GetCompositionLayerPose(ref CompositionLayerParamsQuad.pose); //Update isHeadLock
@@ -670,19 +867,25 @@ namespace VIVE.OpenXR.CompositionLayer
quadSize.width = m_QuadWidth; quadSize.width = m_QuadWidth;
quadSize.height = m_QuadHeight; quadSize.height = m_QuadHeight;
CompositionLayerParamsQuad.size = quadSize; CompositionLayerParamsQuad.size = quadSize;
return CompositionLayerParamsQuad; return CompositionLayerParamsQuad;
} }
XrCompositionLayerCylinderKHR CompositionLayerParamsCylinder = new XrCompositionLayerCylinderKHR(); XrCompositionLayerCylinderKHR CompositionLayerParamsCylinder = new XrCompositionLayerCylinderKHR();
private XrCompositionLayerCylinderKHR AssignCompositionLayerParamsCylinder() private XrCompositionLayerCylinderKHR AssignCompositionLayerParamsCylinder(int eyeid, bool botheye)
{ {
compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>(); compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>();
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;
@@ -733,7 +936,14 @@ namespace VIVE.OpenXR.CompositionLayer
break; break;
} }
CompositionLayerParamsCylinder.subImage.imageRect = layerTextures.textureLayout; if (!botheye) {
if (eyeid == 0)
CompositionLayerParamsQuad.eyeVisibility = XrEyeVisibility.XR_EYE_VISIBILITY_LEFT;
else
CompositionLayerParamsQuad.eyeVisibility = XrEyeVisibility.XR_EYE_VISIBILITY_RIGHT;
}
CompositionLayerParamsCylinder.subImage.imageRect = layerTextures[eyeid].textureLayout;
CompositionLayerParamsCylinder.subImage.imageArrayIndex = 0; CompositionLayerParamsCylinder.subImage.imageArrayIndex = 0;
GetCompositionLayerPose(ref CompositionLayerParamsCylinder.pose); GetCompositionLayerPose(ref CompositionLayerParamsCylinder.pose);
CompositionLayerParamsCylinder.radius = m_CylinderRadius; CompositionLayerParamsCylinder.radius = m_CylinderRadius;
@@ -787,6 +997,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)
@@ -854,9 +1072,21 @@ namespace VIVE.OpenXR.CompositionLayer
} }
public void TerminateLayer() public void TerminateLayer()
{
if (layerID != 0)
{ {
DEBUG("TerminateLayer: layerID: " + layerID); DEBUG("TerminateLayer: layerID: " + layerID);
DestroyCompositionLayer(); DestroyCompositionLayer(0);
layerID = 0;
}
if (layerIDRight != 0)
{
DEBUG("TerminateLayer: layerIDRight: " + layerIDRight);
DestroyCompositionLayer(1);
layerIDRight = 0;
}
if (placeholderGenerated && compositionLayerPlaceholderPrefabGO != null) if (placeholderGenerated && compositionLayerPlaceholderPrefabGO != null)
{ {
@@ -872,9 +1102,10 @@ namespace VIVE.OpenXR.CompositionLayer
public bool TextureParamsChanged() public bool TextureParamsChanged()
{ {
if (previousTexture != texture) if (previousTextureLeft != textureLeft || previousTextureRight != textureRight)
{ {
previousTexture = texture; previousTextureLeft = textureLeft;
previousTextureRight = textureRight;
return true; return true;
} }
@@ -1290,7 +1521,14 @@ namespace VIVE.OpenXR.CompositionLayer
return; return;
} }
if (SetLayerTexture()) bool isBotheye = (textureRight == null || textureLeft == textureRight);
if (isCustomRects)
{
isBotheye = false;
}
if (SetLayerTexture(0))
{ {
isLayerReadyForSubmit = true; isLayerReadyForSubmit = true;
} }
@@ -1300,7 +1538,22 @@ namespace VIVE.OpenXR.CompositionLayer
DEBUG("Composition Layer Lifecycle OnBeforeRender: Layer not ready for submit."); DEBUG("Composition Layer Lifecycle OnBeforeRender: Layer not ready for submit.");
return; return;
} }
SubmitCompositionLayer();
if (!isBotheye) {
if (SetLayerTexture(1))
{
isLayerReadyForSubmit = true;
}
if (!isLayerReadyForSubmit)
{
DEBUG("Composition Layer Lifecycle OnBeforeRender: Layer not ready for submit.");
return;
}
}
SubmitCompositionLayer(0, isBotheye);
if (!isBotheye)
SubmitCompositionLayer(1, isBotheye);
isLayerReadyForSubmit = false; //reset flag after submit isLayerReadyForSubmit = false; //reset flag after submit
} }
@@ -1419,6 +1672,9 @@ namespace VIVE.OpenXR.CompositionLayer
generatedUnderlayMeshRenderer = generatedUnderlayMesh.AddComponent<MeshRenderer>(); generatedUnderlayMeshRenderer = generatedUnderlayMesh.AddComponent<MeshRenderer>();
generatedUnderlayMeshFilter = generatedUnderlayMesh.AddComponent<MeshFilter>(); generatedUnderlayMeshFilter = generatedUnderlayMesh.AddComponent<MeshFilter>();
if (solidEffect)
generatedUnderlayMeshRenderer.sharedMaterial = new Material(Shader.Find("VIVE/OpenXR/CompositionLayer/UnderlayAlphaZeroSolid"));
else
generatedUnderlayMeshRenderer.sharedMaterial = new Material(Shader.Find("VIVE/OpenXR/CompositionLayer/UnderlayAlphaZero")); generatedUnderlayMeshRenderer.sharedMaterial = new Material(Shader.Find("VIVE/OpenXR/CompositionLayer/UnderlayAlphaZero"));
generatedUnderlayMeshRenderer.material.mainTexture = texture; generatedUnderlayMeshRenderer.material.mainTexture = texture;
@@ -1441,6 +1697,9 @@ namespace VIVE.OpenXR.CompositionLayer
generatedUnderlayMeshRenderer = generatedUnderlayMesh.AddComponent<MeshRenderer>(); generatedUnderlayMeshRenderer = generatedUnderlayMesh.AddComponent<MeshRenderer>();
generatedUnderlayMeshFilter = generatedUnderlayMesh.AddComponent<MeshFilter>(); generatedUnderlayMeshFilter = generatedUnderlayMesh.AddComponent<MeshFilter>();
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 = new Material(Shader.Find("VIVE/OpenXR/CompositionLayer/UnderlayAlphaZero"));
generatedUnderlayMeshRenderer.material.mainTexture = texture; generatedUnderlayMeshRenderer.material.mainTexture = texture;
@@ -1524,6 +1783,12 @@ namespace VIVE.OpenXR.CompositionLayer
Right = 2, Right = 2,
} }
public enum CustomRectsType
{
LeftRight = 1,
TopDown = 2,
}
#if UNITY_EDITOR #if UNITY_EDITOR
public enum CylinderLayerParamAdjustmentMode public enum CylinderLayerParamAdjustmentMode
{ {
@@ -1839,6 +2104,7 @@ namespace VIVE.OpenXR.CompositionLayer
return radius; return radius;
} }
} }
#endregion #endregion
} }
} }

View File

@@ -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;
@@ -10,6 +11,7 @@ using VIVE.OpenXR.CompositionLayer.Passthrough;
namespace VIVE.OpenXR.CompositionLayer.Passthrough namespace VIVE.OpenXR.CompositionLayer.Passthrough
{ {
[Obsolete("This class is deprecated. Please use PassthroughAPI instead.")]
public static class CompositionLayerPassthroughAPI public static class CompositionLayerPassthroughAPI
{ {
const string LOG_TAG = "CompositionLayerPassthroughAPI"; const string LOG_TAG = "CompositionLayerPassthroughAPI";
@@ -26,7 +28,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 +70,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 = passthroughFeature.CreatePassthroughHTC(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 +186,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 = passthroughFeature.CreatePassthroughHTC(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 +295,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 = passthroughFeature.CreatePassthroughHTC(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 +357,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 +394,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)) if (!passthroughFeature.PassthroughIDList.Contains(passthroughID))
{ {
ERROR("Passthrough to be destroyed not found"); ERROR("Passthrough to be destroyed not found");
return false; return false;
} }
#if UNITY_STANDALONE
XrPassthroughHTC passthrough = passthrough2Layer[passthroughID].passthrough;
passthroughFeature.DestroyPassthroughHTC(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);
return true;
#endif
#if UNITY_ANDROID
return passthroughFeature.HTCPassthrough_DestroyPassthrough(passthroughID); return passthroughFeature.HTCPassthrough_DestroyPassthrough(passthroughID);
#endif
} }
/// <summary> /// <summary>
@@ -277,7 +446,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 +461,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 +522,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 +603,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 +648,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 +683,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 +743,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 +805,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 +847,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 +885,6 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
return passthroughFeature.PassthroughIDList; return passthroughFeature.PassthroughIDList;
} }
#endregion #endregion
} }
} }

View File

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

View File

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

View File

@@ -0,0 +1,46 @@
// Copyright HTC Corporation All Rights Reserved.
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using VIVE.OpenXR.CompositionLayer;
namespace VIVE.OpenXR.Editor.CompositionLayer
{
[CustomEditor(typeof(ViveCompositionLayerExtraSettings))]
internal class ViveCompositionLayerEditorExtraSettings : UnityEditor.Editor
{
//private SerializedProperty SettingsEditorEnableSharpening;
static string PropertyName_SharpeningEnable = "SettingsEditorEnableSharpening";
static GUIContent Label_SharpeningEnable = new GUIContent("Enable Sharpening", "Enable Sharpening.");
SerializedProperty Property_SharpeningEnable;
static string PropertyName_SharpeningLevel = "SettingsEditorSharpeningLevel";
static GUIContent Label_SharpeningLevel = new GUIContent("Sharpening Level", "Select Sharpening Level.");
SerializedProperty Property_SharpeningLevel;
static string PropertyName_SharpeningMode = "SettingsEditorSharpeningMode";
static GUIContent Label_SharpeningMode = new GUIContent("Sharpening Mode", "Select Sharpening Mode.");
SerializedProperty Property_SharpeningMode;
void OnEnable()
{
Property_SharpeningEnable = serializedObject.FindProperty(PropertyName_SharpeningEnable);
Property_SharpeningMode = serializedObject.FindProperty(PropertyName_SharpeningMode);
Property_SharpeningLevel = serializedObject.FindProperty(PropertyName_SharpeningLevel);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(Property_SharpeningEnable, new GUIContent(Label_SharpeningEnable));
EditorGUILayout.PropertyField(Property_SharpeningMode, new GUIContent(Label_SharpeningMode));
EditorGUILayout.PropertyField(Property_SharpeningLevel, new GUIContent(Label_SharpeningLevel));
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

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

View File

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

View File

@@ -0,0 +1,30 @@
// Copyright HTC Corporation All Rights Reserved.
#if UNITY_EDITOR
using UnityEditor;
using VIVE.OpenXR.FrameSynchronization;
namespace VIVE.OpenXR.Editor.FrameSynchronization
{
[CustomEditor(typeof(ViveFrameSynchronization))]
public class ViveFrameSynchronizationEditor : UnityEditor.Editor
{
SerializedProperty m_SynchronizationMode;
private void OnEnable()
{
m_SynchronizationMode = serializedObject.FindProperty("m_SynchronizationMode");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(m_SynchronizationMode);
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

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

View File

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

View File

@@ -0,0 +1,184 @@
// Copyright HTC Corporation All Rights Reserved.
using UnityEngine;
using VIVE.OpenXR.Interaction;
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
namespace VIVE.OpenXR.Editor.Interaction
{
[CustomEditor(typeof(ViveInteractions))]
public class ViveInteractionsEditor : UnityEditor.Editor
{
SerializedProperty m_ViveHandInteraction, m_ViveWristTracker, m_ViveXRTracker;
#if UNITY_ANDROID
SerializedProperty m_KHRHandInteraction;
#endif
private void OnEnable()
{
m_ViveHandInteraction = serializedObject.FindProperty("m_ViveHandInteraction");
m_ViveWristTracker = serializedObject.FindProperty("m_ViveWristTracker");
m_ViveXRTracker = serializedObject.FindProperty("m_ViveXRTracker");
#if UNITY_ANDROID
m_KHRHandInteraction = serializedObject.FindProperty("m_KHRHandInteraction");
#endif
}
public override void OnInspectorGUI()
{
serializedObject.Update();
#region GUI
GUIStyle boxStyleInfo = new GUIStyle(EditorStyles.helpBox);
boxStyleInfo.fontSize = 12;
boxStyleInfo.wordWrap = true;
GUIStyle boxStyleWarning = new GUIStyle(EditorStyles.helpBox);
boxStyleWarning.fontSize = 12;
boxStyleWarning.fontStyle = FontStyle.Bold;
boxStyleInfo.wordWrap = true;
// ViveHandInteraction
GUILayout.BeginHorizontal();
GUILayout.Space(20);
GUILayout.Label(
"The VIVE Hand Interaction feature enables hand selection and squeezing functions of XR_HTC_hand_interaction extension.\n" +
"Please note that enabling this feature impacts runtime performance.",
boxStyleInfo);
GUILayout.EndHorizontal();
EditorGUILayout.PropertyField(m_ViveHandInteraction);
// ViveWristTracker
GUILayout.Space(20);
GUILayout.BeginHorizontal();
GUILayout.Space(20);
GUILayout.Label(
"The VIVE Wrist Tracker feature enables wrist tracker pose and button functions of XR_HTC_vive_wrist_tracker_interaction extension.\n" +
"Please note that enabling this feature impacts runtime performance.",
boxStyleInfo);
GUILayout.EndHorizontal();
EditorGUILayout.PropertyField(m_ViveWristTracker);
// ViveXrTracker
GUILayout.Space(20);
GUILayout.BeginHorizontal();
GUILayout.Space(20);
GUILayout.Label(
"The VIVE XR Tracker feature enables ultimate tracker pose and button functions.\n" +
"WARNING:\n" +
"Please be aware that enabling this feature significantly affects runtime performance.",
boxStyleWarning);
GUILayout.EndHorizontal();
EditorGUILayout.PropertyField(m_ViveXRTracker);
#if UNITY_ANDROID
// ViveHandInteractionExt
GUILayout.Space(20);
GUILayout.BeginHorizontal();
GUILayout.Space(20);
GUILayout.Label(
"The KHR Hand Interaction feature enables hand functions of XR_EXT_hand_interaction extension.\n" +
"Please note that enabling this feature impacts runtime performance.",
boxStyleInfo);
GUILayout.EndHorizontal();
EditorGUILayout.PropertyField(m_KHRHandInteraction);
#endif
#endregion
ViveInteractions myScript = target as ViveInteractions;
if (myScript.enabled)
{
bool viveHandInteraction = myScript.UseViveHandInteraction();
bool viveWristTracker = myScript.UseViveWristTracker();
bool viveXrTracker = myScript.UseViveXrTracker();
bool khrHandInteraction = myScript.UseKhrHandInteraction();
OpenXRSettings settings = null;
#if UNITY_ANDROID
settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
#elif UNITY_STANDALONE
settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Standalone);
#endif
if (settings != null)
{
bool addPathEnumeration = false;
foreach (var feature in settings.GetFeatures<OpenXRInteractionFeature>())
{
if (feature is Hand.ViveHandInteraction) { feature.enabled = viveHandInteraction; }
if (feature is Tracker.ViveWristTracker) { feature.enabled = viveWristTracker; }
if (feature is Tracker.ViveXRTracker)
{
feature.enabled = viveXrTracker;
addPathEnumeration = viveXrTracker;
}
if (feature is Hand.ViveHandInteractionExt) { feature.enabled = khrHandInteraction; }
}
foreach (var feature in settings.GetFeatures<OpenXRFeature>())
{
if (addPathEnumeration && feature is VivePathEnumeration) { feature.enabled = true; }
}
}
}
serializedObject.ApplyModifiedProperties();
}
}
/*public class ViveInteractionsBuildHook : OpenXRFeatureBuildHooks
{
public override int callbackOrder => 1;
public override Type featureType => typeof(VIVEFocus3Feature);
protected override void OnPostGenerateGradleAndroidProjectExt(string path)
{
}
protected override void OnPostprocessBuildExt(BuildReport report)
{
}
protected override void OnPreprocessBuildExt(BuildReport report)
{
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
if (settings != null)
{
foreach (var feature in settings.GetFeatures<OpenXRFeature>())
{
if (feature is ViveInteractions && feature.enabled)
{
bool viveHandInteraction= ((ViveInteractions)feature).UseViveHandInteraction();
bool viveWristTracker = ((ViveInteractions)feature).UseViveWristTracker();
bool viveXrTracker = ((ViveInteractions)feature).UseViveXrTracker();
bool khrHandInteraction = ((ViveInteractions)feature).UseKhrHandInteraction();
Debug.LogFormat($"ViveInteractionsBuildHook() viveHandInteraction: {viveHandInteraction}, viveWristTracker: {viveWristTracker}, viveXrTracker: {viveXrTracker}, khrHandInteraction: {khrHandInteraction}");
EnableInteraction(viveHandInteraction, viveWristTracker, viveXrTracker, khrHandInteraction);
break;
}
}
}
}
private static void EnableInteraction(
bool viveHandInteraction = false,
bool viveWristTracker = false,
bool viveXrTracker = false,
bool khrHandInteraction = false)
{
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
if (settings == null) { return; }
foreach (var feature in settings.GetFeatures<OpenXRInteractionFeature>())
{
if (feature is Hand.ViveHandInteraction) { feature.enabled = viveHandInteraction; Debug.LogFormat($"EnableInteraction() ViveHandInteraction: {feature.enabled}"); }
if (feature is Tracker.ViveWristTracker) { feature.enabled = viveWristTracker; Debug.LogFormat($"EnableInteraction() ViveWristTracker: {feature.enabled}"); }
if (feature is Tracker.ViveXRTracker) { feature.enabled = viveXrTracker; Debug.LogFormat($"EnableInteraction() ViveXRTracker: {feature.enabled}"); }
if (feature is Hand.ViveHandInteractionExt) { feature.enabled = khrHandInteraction; Debug.LogFormat($"EnableInteraction() ViveHandInteractionExt: {feature.enabled}"); }
}
}
}*/
}
#endif

View File

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

View File

@@ -0,0 +1,44 @@
// Copyright HTC Corporation All Rights Reserved.
#if UNITY_EDITOR
using UnityEditor;
namespace VIVE.OpenXR.Editor
{
[CustomEditor(typeof(VIVERig))]
public class VIVERigEditor : UnityEditor.Editor
{
SerializedProperty m_TrackingOrigin, m_CameraOffset, m_CameraHeight, m_ActionAsset;
private void OnEnable()
{
m_TrackingOrigin = serializedObject.FindProperty("m_TrackingOrigin");
m_CameraOffset = serializedObject.FindProperty("m_CameraOffset");
m_CameraHeight = serializedObject.FindProperty("m_CameraHeight");
m_ActionAsset = serializedObject.FindProperty("m_ActionAsset");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
VIVERig myScript = target as VIVERig;
EditorGUILayout.PropertyField(m_TrackingOrigin);
EditorGUILayout.PropertyField(m_CameraOffset);
EditorGUILayout.HelpBox(
"Set the height of camera when the Tracking Origin is Device.",
MessageType.Info);
EditorGUILayout.PropertyField(m_CameraHeight);
#if ENABLE_INPUT_SYSTEM
EditorGUILayout.PropertyField(m_ActionAsset);
#endif
serializedObject.ApplyModifiedProperties();
if (UnityEngine.GUI.changed)
EditorUtility.SetDirty((VIVERig)target);
}
}
}
#endif

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,180 @@
# XR_HTC_anchor XR_HTC_anchor_persistence
## Name String
XR_htc_anchor XR_HTC_anchor_persistence
## Revision
1
## Overview
This document provides an overview of how to use the AnchorManager to manage anchors in an OpenXR application, specifically using the XR_HTC_anchor and XR_HTC_anchor_persistence extensions.
Introduction
Anchors in OpenXR allow applications to track specific points in space over time. The XR_HTC_anchor extension provides the basic functionality for creating and managing anchors, while the XR_HTC_anchor_persistence extension allows anchors to be persisted across sessions. The AnchorManager class simplifies the use of these extensions by providing high-level methods for common operations.
Checking Extension Support
Before using any anchor-related functions, it's important to check if the extensions are supported on the current system.
```csharp
bool isAnchorSupported = AnchorManager.IsSupported();
bool isPersistedAnchorSupported = AnchorManager.IsPersistedAnchorSupported();
```
## Creating and Managing Anchors
### Creating an Anchor
To create a new anchor, use the CreateAnchor method. This method requires a Pose representing the anchor's position and orientation relative to the tracking space, and a name for the anchor.
```csharp
Pose anchorPose = new Pose(new Vector3(0, 0, 0), Quaternion.identity);
AnchorManager.Anchor newAnchor = AnchorManager.CreateAnchor(anchorPose, "MyAnchor");
```
### Getting an Anchor's Name
To retrieve the name of an existing anchor, use the GetSpatialAnchorName method.
```csharp
string anchorName;
bool success = AnchorManager.GetSpatialAnchorName(newAnchor, out anchorName);
if (success) {
Debug.Log("Anchor name: " + anchorName);
}
```
### Tracking Space and Pose
To get the current tracking space, use the GetTrackingSpace method. To retrieve the pose of an anchor relative to the current tracking space, use the GetTrackingSpacePose method.
```csharp
XrSpace trackingSpace = AnchorManager.GetTrackingSpace();
Pose anchorPose;
bool poseValid = AnchorManager.GetTrackingSpacePose(newAnchor, out anchorPose);
if (poseValid) {
Debug.Log("Anchor pose: " + anchorPose.position + ", " + anchorPose.rotation);
}
```
## Persisting Anchors
### Creating a Persisted Anchor Collection
To enable anchor persistence, create a persisted anchor collection using the CreatePersistedAnchorCollection method.
```csharp
Task createCollectionTask = AnchorManager.CreatePersistedAnchorCollection();
createCollectionTask.Wait();
```
### Persisting an Anchor
To persist an anchor, use the PersistAnchor method with the anchor and a unique name for the persisted anchor.
```csharp
string persistedAnchorName = "MyPersistedAnchor";
XrResult result = AnchorManager.PersistAnchor(newAnchor, persistedAnchorName);
if (result == XrResult.XR_SUCCESS) {
Debug.Log("Anchor persisted successfully.");
}
```
### Unpersisting an Anchor
To remove a persisted anchor, use the UnpersistAnchor method with the name of the persisted anchor.
```csharp
XrResult result = AnchorManager.UnpersistAnchor(persistedAnchorName);
if (result == XrResult.XR_SUCCESS) {
Debug.Log("Anchor unpersisted successfully.");
}
```
### Enumerating Persisted Anchors
To get a list of all persisted anchors, use the EnumeratePersistedAnchorNames method.
```csharp
string[] persistedAnchorNames;
XrResult result = AnchorManager.EnumeratePersistedAnchorNames(out persistedAnchorNames);
if (result == XrResult.XR_SUCCESS) {
foreach (var name in persistedAnchorNames) {
Debug.Log("Persisted anchor: " + name);
}
}
```
### Creating an Anchor from a Persisted Anchor
To create an anchor from a persisted anchor, use the CreateSpatialAnchorFromPersistedAnchor method.
```csharp
AnchorManager.Anchor trackableAnchor;
XrResult result = AnchorManager.CreateSpatialAnchorFromPersistedAnchor(persistedAnchorName, "NewAnchor", out trackableAnchor);
if (result == XrResult.XR_SUCCESS) {
Debug.Log("Anchor created from persisted anchor.");
}
```
## Exporting and Importing Persisted Anchors
### Exporting a Persisted Anchor
To export a persisted anchor to a buffer, use the ExportPersistedAnchor method.
```csharp
Task<(XrResult, string, byte[])> exportTask = AnchorManager.ExportPersistedAnchor(persistedAnchorName);
exportTask.Wait();
var (exportResult, exportName, buffer) = exportTask.Result;
if (exportResult == XrResult.XR_SUCCESS) {
// Save buffer to a file or use as needed
File.WriteAllBytes("anchor.pa", buffer);
}
```
### Importing a Persisted Anchor
To import a persisted anchor from a buffer, use the ImportPersistedAnchor method.
```csharp
byte[] buffer = File.ReadAllBytes("anchor.pa");
Task<XrResult> importTask = AnchorManager.ImportPersistedAnchor(buffer);
importTask.Wait();
if (importTask.Result == XrResult.XR_SUCCESS) {
Debug.Log("Anchor imported successfully.");
}
```
### Clearing Persisted Anchors
To clear all persisted anchors, use the ClearPersistedAnchors method.
```csharp
XrResult result = AnchorManager.ClearPersistedAnchors();
if (result == XrResult.XR_SUCCESS) {
Debug.Log("All persisted anchors cleared.");
}
```
## Conclusion
The AnchorManager class simplifies the management of anchors in OpenXR applications. By using the methods provided, you can easily create, persist, and manage anchors, ensuring that spatial data can be maintained across sessions. This document covers the basic operations; for more advanced usage, refer to the OpenXR specification and the implementation details of the AnchorManager class.

View File

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

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: a88ad0ff22dd2484192540d5b8b63ed6 guid: c5cbfbcf56aaffa4fab38659c00c3903
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@@ -0,0 +1,675 @@
// 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;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.OpenXR.Features;
#endif
namespace VIVE.OpenXR.Feature
{
using XrPersistedAnchorCollectionHTC = System.IntPtr;
#if UNITY_EDITOR
[OpenXRFeature(UiName = "VIVE XR Anchor (Beta)",
Desc = "VIVE's implementaion of the XR_HTC_anchor.",
Company = "HTC",
DocumentationLink = "..\\Documentation",
OpenxrExtensionStrings = kOpenxrExtensionString,
Version = "1.0.0",
BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone },
FeatureId = featureId
)]
#endif
public class ViveAnchor : OpenXRFeature
{
public const string kOpenxrExtensionString = "XR_HTC_anchor XR_EXT_future XR_HTC_anchor_persistence";
/// <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.htcanchor";
/// <summary>
/// Enable or disable the persisted anchor feature. Set it only valid in feature settings.
/// </summary>
public bool enablePersistedAnchor = true;
private XrInstance m_XrInstance = 0;
private XrSession session = 0;
private XrSystemId m_XrSystemId = 0;
private bool IsInited = false;
private bool IsPAInited = false;
private bool useFakeData = false;
#region struct, enum, const of this extensions
/// <summary>
/// An application can inspect whether the system is capable of anchor functionality by
/// chaining an XrSystemAnchorPropertiesHTC structure to the XrSystemProperties when calling
/// xrGetSystemProperties.The runtime must return XR_ERROR_FEATURE_UNSUPPORTED if
/// XrSystemAnchorPropertiesHTC::supportsAnchor was XR_FALSE.
/// supportsAnchor indicates if current system is capable of anchor functionality.
/// </summary>
public struct XrSystemAnchorPropertiesHTC
{
public XrStructureType type;
public System.IntPtr next;
public XrBool32 supportsAnchor;
}
/// <summary>
/// name is a null-terminated UTF-8 string whose length is less than or equal to XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct XrSpatialAnchorNameHTC
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public byte[] name;
public XrSpatialAnchorNameHTC(string anchorName)
{
name = new byte[256];
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(anchorName);
Array.Copy(utf8Bytes, name, Math.Min(utf8Bytes.Length, 255));
name[255] = 0;
}
public XrSpatialAnchorNameHTC(XrSpatialAnchorNameHTC anchorName)
{
name = new byte[256];
Array.Copy(anchorName.name, name, 256);
name[255] = 0;
}
public override readonly string ToString() {
if (name == null)
return string.Empty;
return System.Text.Encoding.UTF8.GetString(name).TrimEnd('\0');
}
}
public struct XrSpatialAnchorCreateInfoHTC
{
public XrStructureType type;
public System.IntPtr next;
public XrSpace space;
public XrPosef poseInSpace;
public XrSpatialAnchorNameHTC name;
}
public struct XrPersistedAnchorCollectionAcquireInfoHTC
{
public XrStructureType type;
public System.IntPtr next;
}
public struct XrPersistedAnchorCollectionAcquireCompletionHTC
{
public XrStructureType type;
public System.IntPtr next;
public XrResult futureResult;
public System.IntPtr persistedAnchorCollection;
}
public struct XrSpatialAnchorPersistInfoHTC
{
public XrStructureType type;
public System.IntPtr next;
public XrSpace anchor;
public XrSpatialAnchorNameHTC persistedAnchorName;
}
public struct XrSpatialAnchorFromPersistedAnchorCreateInfoHTC
{
public XrStructureType type;
public System.IntPtr next;
public System.IntPtr persistedAnchorCollection;
public XrSpatialAnchorNameHTC persistedAnchorName;
public XrSpatialAnchorNameHTC spatialAnchorName;
}
public struct XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC
{
public XrStructureType type;
public System.IntPtr next;
public XrResult futureResult;
public XrSpace anchor;
}
public struct XrPersistedAnchorPropertiesGetInfoHTC
{
public XrStructureType type;
public System.IntPtr next;
public uint maxPersistedAnchorCount;
}
#endregion
#region delegates and delegate instances
public delegate XrResult DelegateXrCreateSpatialAnchorHTC(XrSession session, ref XrSpatialAnchorCreateInfoHTC createInfo, ref XrSpace anchor);
public delegate XrResult DelegateXrGetSpatialAnchorNameHTC(XrSpace anchor, ref XrSpatialAnchorNameHTC name);
public delegate XrResult DelegateXrAcquirePersistedAnchorCollectionAsyncHTC(XrSession session, ref XrPersistedAnchorCollectionAcquireInfoHTC acquireInfo, out IntPtr future);
public delegate XrResult DelegateXrAcquirePersistedAnchorCollectionCompleteHTC(IntPtr future, out XrPersistedAnchorCollectionAcquireCompletionHTC completion);
public delegate XrResult DelegateXrReleasePersistedAnchorCollectionHTC(IntPtr persistedAnchorCollection);
public delegate XrResult DelegateXrPersistSpatialAnchorAsyncHTC(XrPersistedAnchorCollectionHTC persistedAnchorCollection, ref XrSpatialAnchorPersistInfoHTC persistInfo, out IntPtr future);
public delegate XrResult DelegateXrPersistSpatialAnchorCompleteHTC(IntPtr future, out FutureWrapper.XrFutureCompletionEXT completion);
public delegate XrResult DelegateXrUnpersistSpatialAnchorHTC(IntPtr persistedAnchorCollection, ref XrSpatialAnchorNameHTC persistedAnchorName);
public delegate XrResult DelegateXrEnumeratePersistedAnchorNamesHTC( IntPtr persistedAnchorCollection, uint persistedAnchorNameCapacityInput, ref uint persistedAnchorNameCountOutput, [Out] XrSpatialAnchorNameHTC[] persistedAnchorNames);
public delegate XrResult DelegateXrCreateSpatialAnchorFromPersistedAnchorAsyncHTC(XrSession session, ref XrSpatialAnchorFromPersistedAnchorCreateInfoHTC spatialAnchorCreateInfo, out IntPtr future);
public delegate XrResult DelegateXrCreateSpatialAnchorFromPersistedAnchorCompleteHTC(IntPtr future, out XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC completion);
public delegate XrResult DelegateXrClearPersistedAnchorsHTC(IntPtr persistedAnchorCollection);
public delegate XrResult DelegateXrGetPersistedAnchorPropertiesHTC(IntPtr persistedAnchorCollection, ref XrPersistedAnchorPropertiesGetInfoHTC getInfo);
public delegate XrResult DelegateXrExportPersistedAnchorHTC(IntPtr persistedAnchorCollection, ref XrSpatialAnchorNameHTC persistedAnchorName, uint dataCapacityInput, ref uint dataCountOutput, [Out] byte[] data);
public delegate XrResult DelegateXrImportPersistedAnchorHTC(IntPtr persistedAnchorCollection, uint dataCount, [In] byte[] data);
public delegate XrResult DelegateXrGetPersistedAnchorNameFromBufferHTC(IntPtr persistedAnchorCollection, uint bufferCount, byte[] buffer, ref XrSpatialAnchorNameHTC name);
DelegateXrCreateSpatialAnchorHTC XrCreateSpatialAnchorHTC;
DelegateXrGetSpatialAnchorNameHTC XrGetSpatialAnchorNameHTC;
DelegateXrAcquirePersistedAnchorCollectionAsyncHTC XrAcquirePersistedAnchorCollectionAsyncHTC;
DelegateXrAcquirePersistedAnchorCollectionCompleteHTC XrAcquirePersistedAnchorCollectionCompleteHTC;
DelegateXrReleasePersistedAnchorCollectionHTC XrReleasePersistedAnchorCollectionHTC;
DelegateXrPersistSpatialAnchorAsyncHTC XrPersistSpatialAnchorAsyncHTC;
DelegateXrPersistSpatialAnchorCompleteHTC XrPersistSpatialAnchorCompleteHTC;
DelegateXrUnpersistSpatialAnchorHTC XrUnpersistSpatialAnchorHTC;
DelegateXrEnumeratePersistedAnchorNamesHTC XrEnumeratePersistedAnchorNamesHTC;
DelegateXrCreateSpatialAnchorFromPersistedAnchorAsyncHTC XrCreateSpatialAnchorFromPersistedAnchorAsyncHTC;
DelegateXrCreateSpatialAnchorFromPersistedAnchorCompleteHTC XrCreateSpatialAnchorFromPersistedAnchorCompleteHTC;
DelegateXrClearPersistedAnchorsHTC XrClearPersistedAnchorsHTC;
DelegateXrGetPersistedAnchorPropertiesHTC XrGetPersistedAnchorPropertiesHTC;
DelegateXrExportPersistedAnchorHTC XrExportPersistedAnchorHTC;
DelegateXrImportPersistedAnchorHTC XrImportPersistedAnchorHTC;
DelegateXrGetPersistedAnchorNameFromBufferHTC XrGetPersistedAnchorNameFromBufferHTC;
#endregion delegates and delegate instances
#region override functions
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
{
// For LocateSpace, need WaitFrame's predictedDisplayTime.
ViveInterceptors.Instance.AddRequiredFunction("xrWaitFrame");
return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
}
/// <inheritdoc />
protected override bool OnInstanceCreate(ulong xrInstance)
{
#if FAKE_DATA
Debug.LogError("ViveAnchor OnInstanceCreate() Use FakeData");
useFakeData = true;
#endif
IsInited = false;
bool ret = true;
ret &= CommonWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
ret &= SpaceWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
if (!ret)
{
Debug.LogError("ViveAnchor OnInstanceCreate() failed.");
return false;
}
//Debug.Log("VIVEAnchor OnInstanceCreate() ");
if (!OpenXRRuntime.IsExtensionEnabled("XR_HTC_anchor") && !useFakeData)
{
Debug.LogWarning("ViveAnchor OnInstanceCreate() XR_HTC_anchor is NOT enabled.");
return false;
}
IsInited = GetXrFunctionDelegates(xrInstance);
if (!IsInited)
{
Debug.LogError("ViveAnchor OnInstanceCreate() failed to get function delegates.");
return false;
}
m_XrInstance = xrInstance;
bool hasFuture = FutureWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
// No error log because future will print.
#if FAKE_DATA
hasFuture = true;
#endif
IsPAInited = false;
bool hasPersistedAnchor = false;
do
{
if (!hasFuture)
{
Debug.LogWarning("ViveAnchor OnInstanceCreate() XR_HTC_anchor_persistence is NOT enabled because no XR_EXT_future.");
hasPersistedAnchor = false;
break;
}
hasPersistedAnchor = enablePersistedAnchor && OpenXRRuntime.IsExtensionEnabled("XR_HTC_anchor_persistence");
#if FAKE_DATA
hasPersistedAnchor = enablePersistedAnchor;
#endif
} while(false);
//Debug.Log("OnInstanceCreate() " + m_XrInstance);
if (hasPersistedAnchor)
IsPAInited = GetXrFunctionDelegatesPersistance(xrInstance);
if (!IsPAInited)
Debug.LogWarning("ViveAnchor OnInstanceCreate() XR_HTC_anchor_persistence is NOT enabled.");
return IsInited;
}
protected override void OnInstanceDestroy(ulong xrInstance)
{
m_XrInstance = 0;
IsInited = false;
IsPAInited = false;
CommonWrapper.Instance.OnInstanceDestroy();
SpaceWrapper.Instance.OnInstanceDestroy();
FutureWrapper.Instance.OnInstanceDestroy();
Debug.Log("ViveAnchor: OnInstanceDestroy()");
}
/// <inheritdoc />
protected override void OnSessionCreate(ulong xrSession)
{
//Debug.Log("ViveAnchor OnSessionCreate() ");
session = xrSession;
}
/// <inheritdoc />
protected override void OnSessionDestroy(ulong xrSession)
{
//Debug.Log("ViveAnchor OnSessionDestroy() ");
session = 0;
}
// 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 inst)
{
Debug.Log("ViveAnchor GetXrFunctionDelegates() ");
bool ret = true;
OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr = CommonWrapper.Instance.GetInstanceProcAddr; // shorter name
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrCreateSpatialAnchorHTC", out XrCreateSpatialAnchorHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrGetSpatialAnchorNameHTC", out XrGetSpatialAnchorNameHTC);
return ret;
}
private bool GetXrFunctionDelegatesPersistance(XrInstance inst)
{
Debug.Log("ViveAnchor GetXrFunctionDelegatesPersistance() ");
bool ret = true;
OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr = CommonWrapper.Instance.GetInstanceProcAddr; // shorter name
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrAcquirePersistedAnchorCollectionAsyncHTC", out XrAcquirePersistedAnchorCollectionAsyncHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrAcquirePersistedAnchorCollectionCompleteHTC", out XrAcquirePersistedAnchorCollectionCompleteHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrReleasePersistedAnchorCollectionHTC", out XrReleasePersistedAnchorCollectionHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrPersistSpatialAnchorAsyncHTC", out XrPersistSpatialAnchorAsyncHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrPersistSpatialAnchorCompleteHTC", out XrPersistSpatialAnchorCompleteHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrUnpersistSpatialAnchorHTC", out XrUnpersistSpatialAnchorHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrEnumeratePersistedAnchorNamesHTC", out XrEnumeratePersistedAnchorNamesHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrCreateSpatialAnchorFromPersistedAnchorAsyncHTC", out XrCreateSpatialAnchorFromPersistedAnchorAsyncHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrCreateSpatialAnchorFromPersistedAnchorCompleteHTC", out XrCreateSpatialAnchorFromPersistedAnchorCompleteHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrClearPersistedAnchorsHTC", out XrClearPersistedAnchorsHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrGetPersistedAnchorPropertiesHTC", out XrGetPersistedAnchorPropertiesHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrExportPersistedAnchorHTC", out XrExportPersistedAnchorHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrImportPersistedAnchorHTC", out XrImportPersistedAnchorHTC);
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrGetPersistedAnchorNameFromBufferHTC", out XrGetPersistedAnchorNameFromBufferHTC);
return ret;
}
#region functions of extension
/// <summary>
/// Helper function to get this feature's properties.
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
/// </summary>
/// <param name="anchorProperties">Output parameter to hold anchor properties.</param>
/// <returns>XrResult indicating success or failure.</returns>
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);
}
/// <summary>
/// The CreateSpatialAnchor function creates a spatial anchor with specified base space and pose in the space.
/// The anchor is represented by an XrSpace and its pose can be tracked via xrLocateSpace.
/// Once the anchor is no longer needed, call xrDestroySpace to erase the anchor.
/// </summary>
/// <param name="createInfo">Information required to create the spatial anchor.</param>
/// <param name="anchor">Output parameter to hold the created anchor.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult CreateSpatialAnchor(XrSpatialAnchorCreateInfoHTC createInfo, out XrSpace anchor)
{
anchor = default;
if (!IsInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
if (session == 0)
return XrResult.XR_ERROR_SESSION_LOST;
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;
}
/// <summary>
/// The GetSpatialAnchorName function retrieves the name of the spatial anchor.
/// </summary>
/// <param name="anchor">The XrSpace representing the anchor.</param>
/// <param name="name">Output parameter to hold the name of the anchor.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult GetSpatialAnchorName(XrSpace anchor, out XrSpatialAnchorNameHTC name)
{
name = new XrSpatialAnchorNameHTC();
if (!IsInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
return XrGetSpatialAnchorNameHTC(anchor, ref name);
}
/// <summary>
/// If the extension is supported and enabled, return true.
/// </summary>
/// <returns>True if persisted anchor extension is supported, false otherwise.</returns>
public bool IsPersistedAnchorSupported()
{
return IsPAInited;
}
/// <summary>
/// Creates a persisted anchor collection. This collection can be used to persist spatial anchors across sessions.
/// Many persisted anchor APIs need a persisted anchor collection to operate.
/// </summary>
/// <param name="future">Output the async future handle. Check the future to get the PersitedAnchorCollection handle.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult AcquirePersistedAnchorCollectionAsync(out IntPtr future)
{
future = IntPtr.Zero;
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
if (session == 0)
return XrResult.XR_ERROR_SESSION_LOST;
XrPersistedAnchorCollectionAcquireInfoHTC acquireInfo = new XrPersistedAnchorCollectionAcquireInfoHTC
{
type = XrStructureType.XR_TYPE_PERSISTED_ANCHOR_COLLECTION_ACQUIRE_INFO_HTC,
next = IntPtr.Zero,
};
return XrAcquirePersistedAnchorCollectionAsyncHTC(session, ref acquireInfo, out future);
}
public XrResult AcquirePersistedAnchorCollectionComplete(IntPtr future, out XrPersistedAnchorCollectionAcquireCompletionHTC completion)
{
completion = new XrPersistedAnchorCollectionAcquireCompletionHTC();
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
return XrAcquirePersistedAnchorCollectionCompleteHTC(future, out completion);
}
/// <summary>
/// Destroys the persisted anchor collection.
/// </summary>
/// <param name="persistedAnchorCollection">The persisted anchor collection to be destroyed.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult ReleasePersistedAnchorCollection(IntPtr persistedAnchorCollection)
{
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
return XrReleasePersistedAnchorCollectionHTC(persistedAnchorCollection);
}
/// <summary>
/// Persists a spatial anchor with the given name. The name should be unique.
/// </summary>
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
/// <param name="anchor">The spatial anchor to be persisted.</param>
/// <param name="name">The name of the persisted anchor.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult PersistSpatialAnchorAsync(IntPtr persistedAnchorCollection, XrSpace anchor, XrSpatialAnchorNameHTC name, out IntPtr future)
{
future = IntPtr.Zero;
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
XrSpatialAnchorPersistInfoHTC persistInfo = new XrSpatialAnchorPersistInfoHTC
{
type = XrStructureType.XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_HTC,
anchor = anchor,
persistedAnchorName = name
};
return XrPersistSpatialAnchorAsyncHTC(persistedAnchorCollection, ref persistInfo, out future);
}
public XrResult PersistSpatialAnchorComplete(IntPtr future, out FutureWrapper.XrFutureCompletionEXT completion)
{
completion = new FutureWrapper.XrFutureCompletionEXT() {
type = XrStructureType.XR_TYPE_FUTURE_COMPLETION_EXT,
next = IntPtr.Zero,
futureResult = XrResult.XR_SUCCESS
};
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
return XrPersistSpatialAnchorCompleteHTC(future, out completion);
}
/// <summary>
/// Unpersists the anchor with the given name.
/// </summary>
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
/// <param name="name">The name of the anchor to be unpersisted.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult UnpersistSpatialAnchor(IntPtr persistedAnchorCollection, XrSpatialAnchorNameHTC name)
{
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
return XrUnpersistSpatialAnchorHTC(persistedAnchorCollection, ref name);
}
/// <summary>
/// Enumerates all persisted anchor names.
/// </summary>
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
/// <param name="persistedAnchorNameCapacityInput">The capacity of the input buffer.</param>
/// <param name="persistedAnchorNameCountOutput">Output parameter to hold the count of persisted anchor names.</param>
/// <param name="persistedAnchorNames">Output parameter to hold the names of persisted anchors.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult EnumeratePersistedAnchorNames(IntPtr persistedAnchorCollection, uint persistedAnchorNameCapacityInput,
ref uint persistedAnchorNameCountOutput, ref XrSpatialAnchorNameHTC[] persistedAnchorNames)
{
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
return XrEnumeratePersistedAnchorNamesHTC(persistedAnchorCollection, persistedAnchorNameCapacityInput, ref persistedAnchorNameCountOutput, persistedAnchorNames);
}
/// <summary>
/// Creates a spatial anchor from a persisted anchor.
/// </summary>
/// <param name="spatialAnchorCreateInfo">Information required to create the spatial anchor from persisted anchor.</param>
/// <param name="anchor">Output parameter to hold the created spatial anchor.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult CreateSpatialAnchorFromPersistedAnchorAsync(XrSpatialAnchorFromPersistedAnchorCreateInfoHTC spatialAnchorCreateInfo, out IntPtr future)
{
future = IntPtr.Zero;
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
if (session == 0)
return XrResult.XR_ERROR_SESSION_LOST;
return XrCreateSpatialAnchorFromPersistedAnchorAsyncHTC(session, ref spatialAnchorCreateInfo, out future);
}
/// <summary>
/// When the future is ready, call this function to get the result.
/// </summary>
/// <param name="future"></param>
/// <param name="completion"></param>
/// <returns></returns>
public XrResult CreateSpatialAnchorFromPersistedAnchorComplete(IntPtr future, out XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC completion)
{
completion = new XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC()
{
type = XrStructureType.XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_COMPLETION_HTC,
next = IntPtr.Zero,
futureResult = XrResult.XR_SUCCESS,
anchor = 0
};
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
return XrCreateSpatialAnchorFromPersistedAnchorCompleteHTC(future, out completion);
}
/// <summary>
/// Clears all persisted anchors.
/// </summary>
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult ClearPersistedAnchors(IntPtr persistedAnchorCollection)
{
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
return XrClearPersistedAnchorsHTC(persistedAnchorCollection);
}
/// <summary>
/// Gets the properties of the persisted anchor.
/// </summary>
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
/// <param name="getInfo">Output parameter to hold the properties of the persisted anchor.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult GetPersistedAnchorProperties(IntPtr persistedAnchorCollection, out XrPersistedAnchorPropertiesGetInfoHTC getInfo)
{
getInfo = new XrPersistedAnchorPropertiesGetInfoHTC
{
type = XrStructureType.XR_TYPE_PERSISTED_ANCHOR_PROPERTIES_GET_INFO_HTC
};
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
return XrGetPersistedAnchorPropertiesHTC(persistedAnchorCollection, ref getInfo);
}
/// <summary>
/// Exports the persisted anchor to a buffer. The buffer can be used to import the anchor later or save to a file.
/// </summary>
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
/// <param name="persistedAnchorName">The name of the persisted anchor to be exported.</param>
/// <param name="data">Output parameter to hold the buffer containing the exported anchor.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult ExportPersistedAnchor(IntPtr persistedAnchorCollection, XrSpatialAnchorNameHTC persistedAnchorName, out byte[] data)
{
data = null;
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
uint dataCountOutput = 0;
uint dataCapacityInput = 0;
XrResult ret = XrExportPersistedAnchorHTC(persistedAnchorCollection, ref persistedAnchorName, dataCapacityInput, ref dataCountOutput, null);
if (ret != XrResult.XR_SUCCESS)
{
Debug.LogError("ExportPersistedAnchor failed to get data size. ret=" + ret);
data = null;
return ret;
}
dataCapacityInput = dataCountOutput;
data = new byte[dataCountOutput];
ret = XrExportPersistedAnchorHTC(persistedAnchorCollection, ref persistedAnchorName, dataCapacityInput, ref dataCountOutput, data);
return ret;
}
/// <summary>
/// Imports the persisted anchor from a buffer. The buffer should be created by ExportPersistedAnchor.
/// </summary>
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
/// <param name="data">The buffer containing the persisted anchor data.</param>
/// <returns>XrResult indicating success or failure.</returns>
public XrResult ImportPersistedAnchor(IntPtr persistedAnchorCollection, byte[] data)
{
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
return XrImportPersistedAnchorHTC(persistedAnchorCollection, (uint)data.Length, data);
}
/// <summary>
/// Gets the name of the persisted anchor from a buffer. The buffer should be created by ExportPersistedAnchor.
/// </summary>
/// <param name="persistedAnchorCollection"></param>
/// <param name="buffer"></param>
/// <param name="name"></param>
/// <returns></returns>
public XrResult GetPersistedAnchorNameFromBuffer(IntPtr persistedAnchorCollection, byte[] buffer, out XrSpatialAnchorNameHTC name)
{
name = new XrSpatialAnchorNameHTC();
if (!IsPAInited)
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
if (buffer == null)
return XrResult.XR_ERROR_VALIDATION_FAILURE;
return XrGetPersistedAnchorNameFromBufferHTC(persistedAnchorCollection, (uint)buffer.Length, buffer, 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
}
}

View File

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

View File

@@ -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);

View File

@@ -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)

View File

@@ -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)

View File

@@ -0,0 +1,203 @@
// Copyright HTC Corporation All Rights Reserved.
using UnityEditor;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
using UnityEngine;
using System;
using System.Runtime.InteropServices;
#if UNITY_EDITOR
using UnityEditor.XR.OpenXR.Features;
#endif
namespace VIVE.OpenXR.CompositionLayer
{
#if UNITY_EDITOR
[OpenXRFeature(UiName = "VIVE XR Composition Layer (Extra Settings) (Beta)",
Desc = "Enable this feature to use the Composition Layer Extra Settings.",
Company = "HTC",
DocumentationLink = "..\\Documentation",
OpenxrExtensionStrings = kOpenxrExtensionStrings,
Version = "1.0.0",
BuildTargetGroups = new[] { BuildTargetGroup.Android },
FeatureId = featureId
)]
#endif
public class ViveCompositionLayerExtraSettings : OpenXRFeature
{
const string LOG_TAG = "VIVE.OpenXR.ViveCompositionLayer.ExtraSettings";
static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
static void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
/// <summary>
/// Settings Editor Enable Sharpening or Not.
/// </summary>
public bool SettingsEditorEnableSharpening = false;
/// <summary>
/// Support Sharpening or Not.
/// </summary>
public bool supportSharpening = false;
/// <summary>
/// Settings Editor Sharpening Mode
/// </summary>
public XrSharpeningModeHTC SettingsEditorSharpeningMode = XrSharpeningModeHTC.FAST;
/// <summary>
/// Settings Editor Sharpening Levell
/// </summary>
[Range(0.0f, 1.0f)]
public float SettingsEditorSharpeningLevel = 1.0f;
/// <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.compositionlayer.extrasettings";
/// <summary>
/// OpenXR specification.
/// </summary>
public const string kOpenxrExtensionStrings = "XR_HTC_composition_layer_extra_settings";
#region OpenXR Life Cycle
private bool m_XrInstanceCreated = false;
/// <summary>
/// The XR instance is created or not.
/// </summary>
public bool XrInstanceCreated
{
get { return m_XrInstanceCreated; }
}
private XrInstance m_XrInstance = 0;
protected override bool OnInstanceCreate(ulong xrInstance)
{
foreach (string kOpenxrExtensionString in kOpenxrExtensionStrings.Split(' '))
{
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
{
WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
}
}
m_XrInstanceCreated = true;
m_XrInstance = xrInstance;
DEBUG("OnInstanceCreate() " + m_XrInstance);
return true;
}
protected override void OnInstanceDestroy(ulong xrInstance)
{
m_XrInstanceCreated = false;
DEBUG("OnInstanceDestroy() " + m_XrInstance);
}
private XrSystemId m_XrSystemId = 0;
protected override void OnSystemChange(ulong xrSystem)
{
m_XrSystemId = xrSystem;
DEBUG("OnSystemChange() " + m_XrSystemId);
}
private bool m_XrSessionCreated = false;
/// <summary>
/// The XR session is created or not.
/// </summary>
public bool XrSessionCreated
{
get { return m_XrSessionCreated; }
}
private XrSession m_XrSession = 0;
protected override void OnSessionCreate(ulong xrSession)
{
m_XrSession = xrSession;
m_XrSessionCreated = true;
DEBUG("OnSessionCreate() " + m_XrSession);
}
private bool m_XrSessionEnding = false;
/// <summary>
/// The XR session is ending or not.
/// </summary>
public bool XrSessionEnding
{
get { return m_XrSessionEnding; }
}
protected override void OnSessionBegin(ulong xrSession)
{
m_XrSessionEnding = false;
DEBUG("OnSessionBegin() " + m_XrSession);
//enable Sharpening
if (OpenXRRuntime.IsExtensionEnabled("XR_HTC_composition_layer_extra_settings"))
{
ViveCompositionLayer_UpdateSystemProperties(m_XrInstance, m_XrSystemId);
supportSharpening = ViveCompositionLayer_IsSupportSharpening();
if (supportSharpening && SettingsEditorEnableSharpening)
{
EnableSharpening(SettingsEditorSharpeningMode, SettingsEditorSharpeningLevel);
}
}
}
protected override void OnSessionEnd(ulong xrSession)
{
m_XrSessionEnding = true;
DEBUG("OnSessionEnd() " + m_XrSession);
}
protected override void OnSessionDestroy(ulong xrSession)
{
m_XrSessionCreated = false;
DEBUG("OnSessionDestroy() " + xrSession);
}
#endregion
#region Wrapper Functions
private const string ExtLib = "viveopenxr";
[DllImportAttribute(ExtLib, EntryPoint = "viveCompositionLayer_UpdateSystemProperties")]
private static extern int VIVEOpenXR_ViveCompositionLayer_UpdateSystemProperties(XrInstance instance, XrSystemId system_id);
private int ViveCompositionLayer_UpdateSystemProperties(XrInstance instance, XrSystemId system_id)
{
return VIVEOpenXR_ViveCompositionLayer_UpdateSystemProperties(instance, system_id);
}
[DllImportAttribute(ExtLib, EntryPoint = "viveCompositionLayer_IsSupportSharpening")]
private static extern bool VIVEOpenXR_ViveCompositionLayer_IsSupportSharpening();
private bool ViveCompositionLayer_IsSupportSharpening()
{
return VIVEOpenXR_ViveCompositionLayer_IsSupportSharpening();
}
[DllImportAttribute(ExtLib, EntryPoint = "viveCompositionLayer_enableSharpening")]
private static extern int VIVEOpenXR_ViveCompositionLayer_enableSharpening(XrSharpeningModeHTC sharpeningMode, float sharpeningLevel);
/// <summary>
/// Enable the sharpening setting applying to the projection layer.
/// </summary>
/// <param name="sharpeningMode">The sharpening mode in <see cref="XrSharpeningModeHTC"/>.</param>
/// <param name="sharpeningLevel">The sharpening level in float [0, 1].</param>
/// <returns>True for success.</returns>
public bool EnableSharpening(XrSharpeningModeHTC sharpeningMode, float sharpeningLevel)
{
return (VIVEOpenXR_ViveCompositionLayer_enableSharpening(sharpeningMode, sharpeningLevel) == 0);
}
[DllImportAttribute(ExtLib, EntryPoint = "viveCompositionLayer_disableSharpening")]
private static extern int VIVEOpenXR_ViveCompositionLayer_DisableSharpening();
/// <summary>
/// Disable the sharpening setting on the projection layer.
/// </summary>
/// <returns>True for success</returns>
public bool DisableSharpening()
{
return (VIVEOpenXR_ViveCompositionLayer_DisableSharpening() == 0);
}
#endregion
}
}

View File

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

View File

@@ -1,4 +1,4 @@
// Copyright HTC Corporation All Rights Reserved. // Copyright HTC Corporation All Rights Reserved.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -67,65 +67,6 @@ namespace VIVE.OpenXR.CompositionLayer
} }
} }
public struct XrCompositionLayerFlags : IEquatable<UInt64>
{
private readonly UInt64 value;
public XrCompositionLayerFlags(UInt64 u)
{
value = u;
}
public static implicit operator UInt64(XrCompositionLayerFlags xrBool)
{
return xrBool.value;
}
public static implicit operator XrCompositionLayerFlags(UInt64 u)
{
return new XrCompositionLayerFlags(u);
}
public bool Equals(XrCompositionLayerFlags other)
{
return value == other.value;
}
public bool Equals(UInt64 other)
{
return value == other;
}
public override bool Equals(object obj)
{
return obj is XrCompositionLayerFlags && Equals((XrCompositionLayerFlags)obj);
}
public override int GetHashCode()
{
return value.GetHashCode();
}
public override string ToString()
{
return value.ToString();
}
public static bool operator ==(XrCompositionLayerFlags a, XrCompositionLayerFlags b) { return a.Equals(b); }
public static bool operator !=(XrCompositionLayerFlags a, XrCompositionLayerFlags b) { return !a.Equals(b); }
public static bool operator >=(XrCompositionLayerFlags a, XrCompositionLayerFlags b) { return a.value >= b.value; }
public static bool operator <=(XrCompositionLayerFlags a, XrCompositionLayerFlags b) { return a.value <= b.value; }
public static bool operator >(XrCompositionLayerFlags a, XrCompositionLayerFlags b) { return a.value > b.value; }
public static bool operator <(XrCompositionLayerFlags a, XrCompositionLayerFlags b) { return a.value < b.value; }
public static XrCompositionLayerFlags operator +(XrCompositionLayerFlags a, XrCompositionLayerFlags b) { return a.value + b.value; }
public static XrCompositionLayerFlags operator -(XrCompositionLayerFlags a, XrCompositionLayerFlags b) { return a.value - b.value; }
public static XrCompositionLayerFlags operator *(XrCompositionLayerFlags a, XrCompositionLayerFlags b) { return a.value * b.value; }
public static XrCompositionLayerFlags operator /(XrCompositionLayerFlags a, XrCompositionLayerFlags b)
{
if (b.value == 0)
{
throw new DivideByZeroException();
}
return a.value / b.value;
}
}
public struct XrSwapchainCreateFlags : IEquatable<UInt64> public struct XrSwapchainCreateFlags : IEquatable<UInt64>
{ {
@@ -288,6 +229,36 @@ namespace VIVE.OpenXR.CompositionLayer
public XrColor4f colorScale; public XrColor4f colorScale;
public XrColor4f colorBias; public XrColor4f colorBias;
} }
[StructLayout(LayoutKind.Sequential)]
public struct XrCompositionLayerSharpeningSettingHTC
{
public XrStructureType type;
public IntPtr next;
public XrSharpeningModeHTC mode;
public float sharpeningLevel;
}
[StructLayout(LayoutKind.Sequential)]
public struct XrCompositionLayerSuperSamplingSettingHTC
{
public XrStructureType type;
public IntPtr next;
public XrSuperSamplingModeHTC mode;
}
public enum XrSharpeningModeHTC
{
FAST = 0,
NORMAL = 1,
QUALITY = 2,
AUTOMATIC = 3,
}
public enum XrSuperSamplingModeHTC
{
FAST = 0,
NORMAL = 1,
QUALITY = 2,
AUTOMATIC = 3,
}
public enum GraphicsAPI public enum GraphicsAPI
{ {
GLES3 = 1, GLES3 = 1,
@@ -301,8 +272,265 @@ 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 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 layers 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;

View File

@@ -18,16 +18,17 @@ using UnityEditor.XR.OpenXR.Features;
namespace VIVE.OpenXR.CompositionLayer.Passthrough namespace VIVE.OpenXR.CompositionLayer.Passthrough
{ {
#if UNITY_EDITOR #if UNITY_EDITOR
[OpenXRFeature(UiName = "VIVE XR Composition Layer (Passthrough)", [OpenXRFeature(UiName = "VIVE XR Composition Layer (Passthrough) (Deprecated)",
Desc = "Enable this feature to use the HTC Passthrough feature.", Desc = "Enable this feature to use the HTC Passthrough feature.",
Company = "HTC", Company = "HTC",
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
[Obsolete("This class is deprecated. Please use VivePassthrough instead.")]
public class ViveCompositionLayerPassthrough : OpenXRFeature public class ViveCompositionLayerPassthrough : OpenXRFeature
{ {
const string LOG_TAG = "VIVE.OpenXR.ViveCompositionLayerPassthrough"; const string LOG_TAG = "VIVE.OpenXR.ViveCompositionLayerPassthrough";
@@ -36,6 +37,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 +49,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 +65,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 +212,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 +228,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 +363,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 +381,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 +407,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 +439,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 +482,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 +507,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 +641,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 +685,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 +724,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 +740,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 +756,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 +772,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 +788,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 +804,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 +820,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 +836,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 +852,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 +873,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 +890,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 +938,6 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
return meshSpace; return meshSpace;
} }
#endregion #endregion
} }
} }

View File

@@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
namespace VIVE.OpenXR.CompositionLayer.Passthrough namespace VIVE.OpenXR.CompositionLayer.Passthrough
{ {
[Obsolete("This enumeration is deprecated. Please use XrStructureType instead.")]
//[StructLayout(LayoutKind.Sequential)] //[StructLayout(LayoutKind.Sequential)]
public enum XrStructureTypeHTC public enum XrStructureTypeHTC
{ {
@@ -16,6 +17,7 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC = 1000317004, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC = 1000317004,
} }
[Obsolete("This enumeration is deprecated. Please use VIVE.OpenXR.Passthrough.PassthroughLayerForm instead.")]
public enum PassthroughLayerForm public enum PassthroughLayerForm
{ {
///<summary> Fullscreen Passthrough Form</summary> ///<summary> Fullscreen Passthrough Form</summary>
@@ -24,6 +26,7 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
Projected = 1 Projected = 1
} }
[Obsolete("This enumeration is deprecated. Please use VIVE.OpenXR.Passthrough.ProjectedPassthroughSpaceType instead.")]
public enum ProjectedPassthroughSpaceType public enum ProjectedPassthroughSpaceType
{ {
///<summary> ///<summary>

View File

@@ -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.",
@@ -49,6 +49,13 @@ namespace VIVE.OpenXR.DisplayRefreshRate
public const string featureId = "vive.openxr.feature.displayrefreshrate"; public const string featureId = "vive.openxr.feature.displayrefreshrate";
#region OpenXR Life Cycle #region OpenXR Life Cycle
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
{
ViveInterceptors.Instance.AddRequiredFunction("xrPollEvent");
return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
}
private XrInstance m_XrInstance = 0; private XrInstance m_XrInstance = 0;
/// <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.

View File

@@ -0,0 +1,111 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
namespace VIVE.OpenXR.DisplayRefreshRate
{
// -------------------- 12.52. XR_FB_display_refresh_rate --------------------
#region New Structures
/// <summary>
/// On platforms which support dynamically adjusting the display refresh rate, application developers may request a specific display refresh rate in order to improve the overall user experience.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct XrEventDataDisplayRefreshRateChangedFB
{
/// <summary>
/// The <see cref="XrStructureType"/> 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>
/// fromDisplayRefreshRate is the previous display refresh rate.
/// </summary>
public float fromDisplayRefreshRate;
/// <summary>
/// toDisplayRefreshRate is the new display refresh rate.
/// </summary>
public float toDisplayRefreshRate;
/// <summary>
/// The XR_FB_display_refresh_rate extension must be enabled prior to using XrEventDataDisplayRefreshRateChangedFB.
/// </summary>
public XrEventDataDisplayRefreshRateChangedFB(XrStructureType in_type, IntPtr in_next, float in_fromDisplayRefreshRate, float in_toDisplayRefreshRate)
{
type = in_type;
next = in_next;
fromDisplayRefreshRate = in_fromDisplayRefreshRate;
toDisplayRefreshRate = in_toDisplayRefreshRate;
}
/// <summary>
/// Retrieves the identity value of XrEventDataDisplayRefreshRateChangedFB.
/// </summary>
public static XrEventDataDisplayRefreshRateChangedFB identity
{
get
{
return new XrEventDataDisplayRefreshRateChangedFB(XrStructureType.XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB, IntPtr.Zero, 0.0f, 0.0f); // user is default present
}
}
public static bool Get(XrEventDataBuffer eventDataBuffer, out XrEventDataDisplayRefreshRateChangedFB eventDataDisplayRefreshRateChangedFB)
{
eventDataDisplayRefreshRateChangedFB = identity;
if (eventDataBuffer.type == XrStructureType.XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB)
{
eventDataDisplayRefreshRateChangedFB.next = eventDataBuffer.next;
eventDataDisplayRefreshRateChangedFB.fromDisplayRefreshRate = BitConverter.ToSingle(eventDataBuffer.varying, 0);
eventDataDisplayRefreshRateChangedFB.toDisplayRefreshRate = BitConverter.ToSingle(eventDataBuffer.varying, 4);
return true;
}
return false;
}
}
public static class ViveDisplayRefreshRateChanged
{
public delegate void OnDisplayRefreshRateChanged(float fromDisplayRefreshRate, float toDisplayRefreshRate);
public static void Listen(OnDisplayRefreshRateChanged callback)
{
if (!allEventListeners.Contains(callback))
allEventListeners.Add(callback);
}
public static void Remove(OnDisplayRefreshRateChanged callback)
{
if (allEventListeners.Contains(callback))
allEventListeners.Remove(callback);
}
public static void Send(float fromDisplayRefreshRate, float toDisplayRefreshRate)
{
int N = 0;
if (allEventListeners != null)
{
N = allEventListeners.Count;
for (int i = N - 1; i >= 0; i--)
{
OnDisplayRefreshRateChanged single = allEventListeners[i];
try
{
single(fromDisplayRefreshRate, toDisplayRefreshRate);
}
catch (Exception e)
{
Debug.Log("Event : " + e.ToString());
allEventListeners.Remove(single);
Debug.Log("Event : A listener is removed due to exception.");
}
}
}
}
private static List<OnDisplayRefreshRateChanged> allEventListeners = new List<OnDisplayRefreshRateChanged>();
}
#endregion
}

View File

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

View File

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

View File

@@ -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 (Beta)",
BuildTargetGroups = new[] { BuildTargetGroup.Android, 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;
}
}
}

View File

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

View File

@@ -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);
}
}

View File

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

View File

@@ -51,10 +51,10 @@ Through feeding the blend shape values of lip expression to an avatar, its facia
XR_LIP_EXPRESSION_MOUTH_UPPER_OVERTURN_HTC = 9, XR_LIP_EXPRESSION_MOUTH_UPPER_OVERTURN_HTC = 9,
XR_LIP_EXPRESSION_MOUTH_LOWER_OVERTURN_HTC = 10, XR_LIP_EXPRESSION_MOUTH_LOWER_OVERTURN_HTC = 10,
XR_LIP_EXPRESSION_MOUTH_POUT_HTC = 11, XR_LIP_EXPRESSION_MOUTH_POUT_HTC = 11,
XR_LIP_EXPRESSION_MOUTH_SMILE_RIGHT_HTC = 12, XR_LIP_EXPRESSION_MOUTH_RAISER_RIGHT_HTC = 12,
XR_LIP_EXPRESSION_MOUTH_SMILE_LEFT_HTC = 13, XR_LIP_EXPRESSION_MOUTH_RAISER_LEFT_HTC = 13,
XR_LIP_EXPRESSION_MOUTH_SAD_RIGHT_HTC = 14, XR_LIP_EXPRESSION_MOUTH_STRETCHER_RIGHT_HTC = 14,
XR_LIP_EXPRESSION_MOUTH_SAD_LEFT_HTC = 15, XR_LIP_EXPRESSION_MOUTH_STRETCHER_LEFT_HTC = 15,
XR_LIP_EXPRESSION_CHEEK_PUFF_RIGHT_HTC = 16, XR_LIP_EXPRESSION_CHEEK_PUFF_RIGHT_HTC = 16,
XR_LIP_EXPRESSION_CHEEK_PUFF_LEFT_HTC = 17, XR_LIP_EXPRESSION_CHEEK_PUFF_LEFT_HTC = 17,
XR_LIP_EXPRESSION_CHEEK_SUCK_HTC = 18, XR_LIP_EXPRESSION_CHEEK_SUCK_HTC = 18,

View File

@@ -67,9 +67,12 @@ namespace VIVE.OpenXR.FacialTracking
/// </summary> /// </summary>
/// <param name="xrInstance">The instance to destroy.</param> /// <param name="xrInstance">The instance to destroy.</param>
protected override void OnInstanceDestroy(ulong xrInstance) protected override void OnInstanceDestroy(ulong xrInstance)
{
if (m_XrInstance == xrInstance)
{ {
m_XrInstanceCreated = false; m_XrInstanceCreated = false;
m_XrInstance = 0; m_XrInstance = 0;
}
DEBUG("OnInstanceDestroy() " + xrInstance); DEBUG("OnInstanceDestroy() " + xrInstance);
} }

View File

@@ -201,19 +201,19 @@ namespace VIVE.OpenXR.FacialTracking
/// <summary> /// <summary>
/// This blend shape raises the right side of the mouth further with a higher value. /// This blend shape raises the right side of the mouth further with a higher value.
/// </summary> /// </summary>
XR_LIP_EXPRESSION_MOUTH_SMILE_RIGHT_HTC = 12, XR_LIP_EXPRESSION_MOUTH_RAISER_RIGHT_HTC = 12,
/// <summary> /// <summary>
/// This blend shape raises the left side of the mouth further with a higher value. /// This blend shape raises the left side of the mouth further with a higher value.
/// </summary> /// </summary>
XR_LIP_EXPRESSION_MOUTH_SMILE_LEFT_HTC = 13, XR_LIP_EXPRESSION_MOUTH_RAISER_LEFT_HTC = 13,
/// <summary> /// <summary>
/// This blend shape lowers the right side of the mouth further with a higher value. /// This blend shape lowers the right side of the mouth further with a higher value.
/// </summary> /// </summary>
XR_LIP_EXPRESSION_MOUTH_SAD_RIGHT_HTC = 14, XR_LIP_EXPRESSION_MOUTH_STRETCHER_RIGHT_HTC = 14,
/// <summary> /// <summary>
/// This blend shape lowers the left side of the mouth further with a higher value. /// This blend shape lowers the left side of the mouth further with a higher value.
/// </summary> /// </summary>
XR_LIP_EXPRESSION_MOUTH_SAD_LEFT_HTC = 15, XR_LIP_EXPRESSION_MOUTH_STRETCHER_LEFT_HTC = 15,
/// <summary> /// <summary>
/// This blend shape puffs up the right side of the cheek further with a higher value. /// This blend shape puffs up the right side of the cheek further with a higher value.
/// </summary> /// </summary>
@@ -433,7 +433,7 @@ namespace VIVE.OpenXR.FacialTracking
/// </summary> /// </summary>
/// <param name="facialTracker">An <see cref="XrFacialTrackerHTC">XrFacialTrackerHTC</see> previously created by <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateFacialTrackerHTC">xrCreateFacialTrackerHTC</see>.</param> /// <param name="facialTracker">An <see cref="XrFacialTrackerHTC">XrFacialTrackerHTC</see> previously created by <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateFacialTrackerHTC">xrCreateFacialTrackerHTC</see>.</param>
/// <param name="facialExpressions">A pointer to <see cref="XrFacialExpressionsHTC">XrFacialExpressionsHTC</see> receiving the returned facial expressions.</param> /// <param name="facialExpressions">A pointer to <see cref="XrFacialExpressionsHTC">XrFacialExpressionsHTC</see> receiving the returned facial expressions.</param>
/// <returns></returns> /// <returns>XR_SUCCESS for success.</returns>
public delegate XrResult xrGetFacialExpressionsHTCDelegate( public delegate XrResult xrGetFacialExpressionsHTCDelegate(
XrFacialTrackerHTC facialTracker, XrFacialTrackerHTC facialTracker,
ref XrFacialExpressionsHTC facialExpressions); ref XrFacialExpressionsHTC facialExpressions);

View File

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

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: c44051e3658496a428f4b92daa752ebf guid: e80a989be51974a4e88bdc41872d53c9
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@@ -0,0 +1,185 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Runtime.InteropServices;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.OpenXR.Features;
#endif
using VIVE.OpenXR.SecondaryViewConfiguration;
namespace VIVE.OpenXR.FirstPersonObserver
{
/// <summary>
/// Name: FirstPersonObserver.cs
/// Role: OpenXR FirstPersonObserver Extension Class
/// Responsibility: The OpenXR extension implementation and its lifecycles logic in OpenXR
/// </summary>
#if UNITY_EDITOR
[OpenXRFeature(UiName = "XR MSFT First Person Observer",
BuildTargetGroups = new[] { BuildTargetGroup.Android },
Company = "HTC",
Desc = "Request the application to render an additional first-person view of the scene.",
DocumentationLink = "..\\Documentation",
OpenxrExtensionStrings = OPEN_XR_EXTENSION_STRING,
Version = "1.0.0",
FeatureId = FeatureId,
Hidden = true)]
#endif
public class ViveFirstPersonObserver : OpenXRFeature
{
private static ViveFirstPersonObserver _instance;
/// <summary>
/// ViveFirstPersonObserver static instance (Singleton).
/// </summary>
public static ViveFirstPersonObserver Instance
{
get
{
if (_instance == null)
{
_instance =
OpenXRSettings.Instance.GetFeature<ViveFirstPersonObserver>();
}
return _instance;
}
}
/// <summary>
/// The log identification.
/// </summary>
private const string LogTag = "VIVE.OpenXR.FirstPersonObserver";
/// <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.firstpersonobserver";
/// <summary>
/// OpenXR specification <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_MSFT_first_person_observer">12.114. XR_MSFT_first_person_observer</see>.
/// </summary>
public const string OPEN_XR_EXTENSION_STRING = "XR_MSFT_first_person_observer";
/// <summary>
/// The flag represents whether the OpenXR loader created an instance or not.
/// </summary>
private bool XrInstanceCreated { get; set; } = false;
/// <summary>
/// The instance created through xrCreateInstance.
/// </summary>
private XrInstance XrInstance { get; set; } = 0;
/// <summary>
/// The function delegate declaration of xrGetInstanceProcAddr.
/// </summary>
private OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr { get; set; }
#region OpenXR life-cycle events
/// <summary>
/// Called after xrCreateInstance.
/// </summary>
/// <param name="xrInstance">Handle of the xrInstance.</param>
/// <returns>Returns true if successful. Returns false otherwise.</returns>
protected override bool OnInstanceCreate(ulong xrInstance)
{
if (!IsExtensionEnabled())
{
Warning($"OnInstanceCreate() {OPEN_XR_EXTENSION_STRING} or " +
$"{ViveSecondaryViewConfiguration.OPEN_XR_EXTENSION_STRING} is NOT enabled.");
return false;
}
XrInstanceCreated = true;
XrInstance = xrInstance;
Debug("OnInstanceCreate() " + XrInstance);
if (!GetXrFunctionDelegates(XrInstance))
{
Error("Get function pointer of OpenXRFunctionPointerAccessor failed.");
return false;
}
Debug("Get function pointer of OpenXRFunctionPointerAccessor succeed.");
return base.OnInstanceCreate(xrInstance);
}
#endregion
/// <summary>
/// Get the OpenXR function via XrInstance.
/// </summary>
/// <param name="xrInstance">The XrInstance is provided by the Unity OpenXR Plugin.</param>
/// <returns>Return true if get successfully. False otherwise.</returns>
private bool GetXrFunctionDelegates(XrInstance xrInstance)
{
if (xrGetInstanceProcAddr != IntPtr.Zero)
{
Debug("Get function pointer of openXRFunctionPointerAccessor.");
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(xrGetInstanceProcAddr,
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
if (XrGetInstanceProcAddr == null)
{
Error(
"Get function pointer of openXRFunctionPointerAccessor failed due to the XrGetInstanceProcAddr is null.");
return false;
}
}
else
{
Error(
"Get function pointer of openXRFunctionPointerAccessor failed due to the xrGetInstanceProcAddr is null.");
return false;
}
return true;
}
#region Utilities functions
/// <summary>
/// Check ViveFirstPersonObserver extension is enabled or not.
/// </summary>
/// <returns>Return true if enabled. False otherwise.</returns>
public static bool IsExtensionEnabled()
{
return OpenXRRuntime.IsExtensionEnabled(OPEN_XR_EXTENSION_STRING) &&
ViveSecondaryViewConfiguration.IsExtensionEnabled();
}
/// <summary>
/// Print log with tag "VIVE.OpenXR.SecondaryViewConfiguration".
/// </summary>
/// <param name="msg">The log you want to print.</param>
private static void Debug(string msg)
{
UnityEngine.Debug.Log(LogTag + " " + msg);
}
/// <summary>
/// Print warning message with tag "VIVE.OpenXR.SecondaryViewConfiguration".
/// </summary>
/// <param name="msg">The warning message you want to print.</param>
private static void Warning(string msg)
{
UnityEngine.Debug.LogWarning(LogTag + " " + msg);
}
/// <summary>
/// Print an error message with the tag "VIVE.OpenXR.SecondaryViewConfiguration."
/// </summary>
/// <param name="msg">The error message you want to print.</param>
private static void Error(string msg)
{
UnityEngine.Debug.LogError(LogTag + " " + msg);
}
#endregion
}
}

View File

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

View File

@@ -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");
@@ -55,12 +85,12 @@ namespace VIVE.OpenXR
private static extern IntPtr intercept_xrGetInstanceProcAddr(IntPtr func); private static extern IntPtr intercept_xrGetInstanceProcAddr(IntPtr func);
[DllImport(ExtLib, EntryPoint = "applyFoveationHTC")] [DllImport(ExtLib, EntryPoint = "applyFoveationHTC")]
private static extern XrResult applyFoveationHTC(XrFoveationModeHTC mode, UInt32 configCount, XrFoveationConfigurationHTC[] configs, UInt64 flags); private static extern XrResult applyFoveationHTC(Foveation.XrFoveationModeHTC mode, UInt32 configCount, Foveation.XrFoveationConfigurationHTC[] configs, UInt64 flags);
/// <summary> /// <summary>
/// function to apply HTC Foveation /// function to apply HTC Foveation
/// </summary> /// </summary>
public static XrResult ApplyFoveationHTC(XrFoveationModeHTC mode, UInt32 configCount, XrFoveationConfigurationHTC[] configs, UInt64 flags = 0) public static XrResult ApplyFoveationHTC(Foveation.XrFoveationModeHTC mode, UInt32 configCount, Foveation.XrFoveationConfigurationHTC[] configs, UInt64 flags = 0)
{ {
//Debug.Log("Unity HTCFoveat:configCount " + configCount); //Debug.Log("Unity HTCFoveat:configCount " + configCount);
//if (configCount >=2) { //if (configCount >=2) {

Some files were not shown because too many files have changed in this diff Show More