Compare commits
5 Commits
versions/2
...
versions/2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae66f9c2fe | ||
|
|
2bfa2ad4c7 | ||
|
|
dfdcd0fd7f | ||
|
|
5ac252bf2e | ||
|
|
fa1969a087 |
4
LICENSE.txt
Normal file
4
LICENSE.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Copyright © HTC Corporation, LLC and its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
Your use of this SDK, sample, or tool is subject to HTC VIVE SDK License Agreement, available at https://developer.vive.com/resources/downloads/licenses-and-agreements/
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -277,18 +281,48 @@ namespace VIVE.OpenXR.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var feature in settings.GetFeatures<OpenXRFeature>())
|
var features = settings.GetFeatures<OpenXRFeature>();
|
||||||
|
foreach (var feature in features)
|
||||||
{
|
{
|
||||||
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 (feature is VIVEFocus3Feature)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < features.Length; i++)
|
||||||
|
{
|
||||||
|
if (features[i] is Enterprise.ViveEnterpriseCommand)
|
||||||
|
{
|
||||||
|
features[i].enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (enableHandtracking)
|
if (enableHandtracking)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -51,6 +51,39 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
static GUIContent Label_QuadHeight = new GUIContent("Height", "Height of a Quad Layer");
|
static GUIContent Label_QuadHeight = new GUIContent("Height", "Height of a Quad Layer");
|
||||||
SerializedProperty Property_QuadHeight;
|
SerializedProperty Property_QuadHeight;
|
||||||
|
|
||||||
|
static string PropertyName_EquirectRadius = "m_EquirectRadius";
|
||||||
|
static GUIContent Label_EquirectRadius = new GUIContent("Radius", "Radius of Equirect Layer");
|
||||||
|
SerializedProperty Property_EquirectRadius;
|
||||||
|
|
||||||
|
static string PropertyName_EquirectScaleX = "m_EquirectScaleX";
|
||||||
|
static GUIContent Label_EquirectScaleX = new GUIContent("scale.x", "Scale.X of Equirect Layer");
|
||||||
|
SerializedProperty Property_EquirectScaleX;
|
||||||
|
|
||||||
|
static string PropertyName_EquirectScaleY = "m_EquirectScaleY";
|
||||||
|
static GUIContent Label_EquirectScaleY = new GUIContent("scale.y", "Scale.Y of Equirect Layer");
|
||||||
|
SerializedProperty Property_EquirectScaleY;
|
||||||
|
|
||||||
|
static string PropertyName_EquirectBiasX = "m_EquirectBiasX";
|
||||||
|
static GUIContent Label_EquirectBiasX = new GUIContent("bias.x", "Bias.X of Equirect Layer");
|
||||||
|
SerializedProperty Property_EquirectBiasX;
|
||||||
|
|
||||||
|
static string PropertyName_EquirectBiasY = "m_EquirectBiasY";
|
||||||
|
static GUIContent Label_EquirectBiasY = new GUIContent("bias.y", "Bias.Y of Equirect Layer");
|
||||||
|
SerializedProperty Property_EquirectBiasY;
|
||||||
|
|
||||||
|
static string PropertyName_EquirectCentralHorizontalAngle = "m_EquirectCentralHorizontalAngle";
|
||||||
|
static GUIContent Label_EquirectCentralHorizontalAngle = new GUIContent("CentralHorizontalAngle", "Central Horizontal Angle of Equirect Layer");
|
||||||
|
SerializedProperty Property_EquirectCentralHorizontalAngle;
|
||||||
|
|
||||||
|
static string PropertyName_EquirectUpperVerticalAngle = "m_EquirectUpperVerticalAngle";
|
||||||
|
static GUIContent Label_EquirectUpperVerticalAngle = new GUIContent("UpperVerticalAngle", "Upper Vertical Angle of Equirect Layer");
|
||||||
|
SerializedProperty Property_EquirectUpperVerticalAngle;
|
||||||
|
|
||||||
|
static string PropertyName_EquirectLowerVerticalAngle = "m_EquirectLowerVerticalAngle";
|
||||||
|
static GUIContent Label_EquirectLowerVerticalAngle = new GUIContent("LowerVerticalAngle", "Lower Vertical Angle of Equirect Layer");
|
||||||
|
SerializedProperty Property_EquirectLowerVerticalAngle;
|
||||||
|
|
||||||
|
|
||||||
static string PropertyName_CylinderHeight = "m_CylinderHeight";
|
static string PropertyName_CylinderHeight = "m_CylinderHeight";
|
||||||
static GUIContent Label_CylinderHeight = new GUIContent("Height", "Height of Cylinder Layer");
|
static GUIContent Label_CylinderHeight = new GUIContent("Height", "Height of Cylinder Layer");
|
||||||
SerializedProperty Property_CylinderHeight;
|
SerializedProperty Property_CylinderHeight;
|
||||||
@@ -83,6 +116,15 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
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;
|
||||||
@@ -114,18 +156,29 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
|
|
||||||
private bool showLayerParams = true, showColorScaleBiasParams = true;
|
private bool showLayerParams = true, showColorScaleBiasParams = true;
|
||||||
|
|
||||||
private bool showExternalSurfaceParams = false;
|
//private bool showExternalSurfaceParams = false;
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (Property_EquirectRadius == null) Property_EquirectRadius = serializedObject.FindProperty(PropertyName_EquirectRadius);
|
||||||
|
if (Property_EquirectScaleX == null) Property_EquirectScaleX = serializedObject.FindProperty(PropertyName_EquirectScaleX);
|
||||||
|
if (Property_EquirectScaleY == null) Property_EquirectScaleY = serializedObject.FindProperty(PropertyName_EquirectScaleY);
|
||||||
|
if (Property_EquirectBiasX == null) Property_EquirectBiasX = serializedObject.FindProperty(PropertyName_EquirectBiasX);
|
||||||
|
if (Property_EquirectBiasY == null) Property_EquirectBiasY = serializedObject.FindProperty(PropertyName_EquirectBiasY);
|
||||||
|
if (Property_EquirectCentralHorizontalAngle == null) Property_EquirectCentralHorizontalAngle = serializedObject.FindProperty(PropertyName_EquirectCentralHorizontalAngle);
|
||||||
|
if (Property_EquirectUpperVerticalAngle == null) Property_EquirectUpperVerticalAngle = serializedObject.FindProperty(PropertyName_EquirectUpperVerticalAngle);
|
||||||
|
if (Property_EquirectLowerVerticalAngle == null) Property_EquirectLowerVerticalAngle = serializedObject.FindProperty(PropertyName_EquirectLowerVerticalAngle);
|
||||||
if (Property_CylinderHeight == null) Property_CylinderHeight = serializedObject.FindProperty(PropertyName_CylinderHeight);
|
if (Property_CylinderHeight == null) Property_CylinderHeight = serializedObject.FindProperty(PropertyName_CylinderHeight);
|
||||||
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);
|
||||||
@@ -134,6 +187,7 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
if (Property_ExternalSurfaceWidth == null) Property_ExternalSurfaceWidth = serializedObject.FindProperty(PropertyName_ExternalSurfaceWidth);
|
if (Property_ExternalSurfaceWidth == null) Property_ExternalSurfaceWidth = serializedObject.FindProperty(PropertyName_ExternalSurfaceWidth);
|
||||||
if (Property_ExternalSurfaceHeight == null) Property_ExternalSurfaceHeight = serializedObject.FindProperty(PropertyName_ExternalSurfaceHeight);
|
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_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);
|
||||||
@@ -158,6 +212,138 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
EditorGUILayout.PropertyField(Property_LayerShape, new GUIContent(Label_LayerShape));
|
EditorGUILayout.PropertyField(Property_LayerShape, new GUIContent(Label_LayerShape));
|
||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
if (Property_LayerShape.intValue == (int)CompositionLayer.LayerShape.Equirect || Property_LayerShape.intValue == (int)CompositionLayer.LayerShape.Equirect2)
|
||||||
|
{
|
||||||
|
if (targetCompositionLayer.isPreviewingQuad)
|
||||||
|
{
|
||||||
|
targetCompositionLayer.isPreviewingQuad = false;
|
||||||
|
if (targetCompositionLayer.generatedPreview != null)
|
||||||
|
{
|
||||||
|
DestroyImmediate(targetCompositionLayer.generatedPreview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetCompositionLayer.isPreviewingCylinder)
|
||||||
|
{
|
||||||
|
targetCompositionLayer.isPreviewingCylinder = false;
|
||||||
|
if (targetCompositionLayer.generatedPreview != null)
|
||||||
|
{
|
||||||
|
DestroyImmediate(targetCompositionLayer.generatedPreview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FeatureHelpers.GetFeatureWithIdForBuildTarget(BuildTargetGroup.Android, ViveCompositionLayerEquirect.featureId).enabled)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("The Composition Layer Equirect feature is not enabled in OpenXR Settings.\nEnable it to use Equirect layers.", MessageType.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.indentLevel++;
|
||||||
|
showLayerParams = EditorGUILayout.Foldout(showLayerParams, "Equirect Parameters");
|
||||||
|
if (showLayerParams)
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(Property_EquirectRadius, new GUIContent(Label_EquirectRadius));
|
||||||
|
|
||||||
|
if (Property_LayerShape.intValue == (int)CompositionLayer.LayerShape.Equirect)
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(Property_EquirectScaleX, new GUIContent(Label_EquirectScaleX));
|
||||||
|
EditorGUILayout.PropertyField(Property_EquirectScaleY, new GUIContent(Label_EquirectScaleY));
|
||||||
|
EditorGUILayout.PropertyField(Property_EquirectBiasX, new GUIContent(Label_EquirectBiasX));
|
||||||
|
EditorGUILayout.PropertyField(Property_EquirectBiasY, new GUIContent(Label_EquirectBiasY));
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Property_LayerShape.intValue == (int)CompositionLayer.LayerShape.Equirect2)
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(Property_EquirectCentralHorizontalAngle, new GUIContent(Label_EquirectCentralHorizontalAngle));
|
||||||
|
EditorGUILayout.PropertyField(Property_EquirectUpperVerticalAngle, new GUIContent(Label_EquirectUpperVerticalAngle));
|
||||||
|
EditorGUILayout.PropertyField(Property_EquirectLowerVerticalAngle, new GUIContent(Label_EquirectLowerVerticalAngle));
|
||||||
|
}
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
EditorGUI.indentLevel--;
|
||||||
|
|
||||||
|
bool EquirectParamsChanged = targetCompositionLayer.LayerDimensionsChanged();
|
||||||
|
if (targetCompositionLayer.isPreviewingEquirect)
|
||||||
|
{
|
||||||
|
Transform generatedPreviewTransform = targetCompositionLayer.transform.Find(CompositionLayer.EquirectPreviewName);
|
||||||
|
|
||||||
|
if (generatedPreviewTransform != null)
|
||||||
|
{
|
||||||
|
targetCompositionLayer.generatedPreview = generatedPreviewTransform.gameObject;
|
||||||
|
|
||||||
|
if (EquirectParamsChanged)
|
||||||
|
{
|
||||||
|
MeshFilter equirectMeshFilter = targetCompositionLayer.generatedPreview.GetComponent<MeshFilter>();
|
||||||
|
|
||||||
|
//Generate vertices
|
||||||
|
equirectMeshFilter.mesh = CompositionLayer.MeshGenerationHelper.GenerateEquirectMesh(targetCompositionLayer.hmd, targetCompositionLayer.EquirectRadius);
|
||||||
|
|
||||||
|
targetCompositionLayer.generatedPreview.transform.localPosition = Vector3.zero;
|
||||||
|
targetCompositionLayer.generatedPreview.transform.localRotation = Quaternion.identity;
|
||||||
|
|
||||||
|
targetCompositionLayer.generatedPreview.transform.localScale = targetCompositionLayer.GetNormalizedLocalScale(targetCompositionLayer.transform, Vector3.one);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetCompositionLayer.generatedPreview.GetComponent<MeshRenderer>().sharedMaterial.mainTexture != targetCompositionLayer.texture)
|
||||||
|
{
|
||||||
|
targetCompositionLayer.generatedPreview.GetComponent<MeshRenderer>().sharedMaterial.mainTexture = targetCompositionLayer.texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Hide Equirect Preview"))
|
||||||
|
{
|
||||||
|
targetCompositionLayer.isPreviewingEquirect = false;
|
||||||
|
if (targetCompositionLayer.generatedPreview != null)
|
||||||
|
{
|
||||||
|
DestroyImmediate(targetCompositionLayer.generatedPreview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetCompositionLayer.isPreviewingEquirect = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("Show Equirect 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.isPreviewingEquirect = true;
|
||||||
|
//Vector3[] cylinderVertices = CompositionLayer.MeshGenerationHelper.GenerateCylinderVertex(targetCompositionLayer.CylinderAngleOfArc, targetCompositionLayer.CylinderRadius, targetCompositionLayer.CylinderHeight);
|
||||||
|
//Add components to Game Object
|
||||||
|
targetCompositionLayer.generatedPreview = new GameObject();
|
||||||
|
targetCompositionLayer.generatedPreview.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
targetCompositionLayer.generatedPreview.name = CompositionLayer.EquirectPreviewName;
|
||||||
|
targetCompositionLayer.generatedPreview.transform.SetParent(targetCompositionLayer.gameObject.transform);
|
||||||
|
targetCompositionLayer.generatedPreview.transform.localPosition = Vector3.zero;
|
||||||
|
targetCompositionLayer.generatedPreview.transform.localRotation = Quaternion.identity;
|
||||||
|
|
||||||
|
targetCompositionLayer.generatedPreview.transform.localScale = targetCompositionLayer.GetNormalizedLocalScale(targetCompositionLayer.transform, Vector3.one);
|
||||||
|
|
||||||
|
MeshRenderer equirectMeshRenderer = targetCompositionLayer.generatedPreview.AddComponent<MeshRenderer>();
|
||||||
|
MeshFilter equirectMeshFilter = targetCompositionLayer.generatedPreview.AddComponent<MeshFilter>();
|
||||||
|
equirectMeshRenderer.sharedMaterial = new Material(Shader.Find("Unlit/Transparent"));
|
||||||
|
|
||||||
|
if (targetCompositionLayer.texture != null)
|
||||||
|
{
|
||||||
|
equirectMeshRenderer.sharedMaterial.mainTexture = targetCompositionLayer.texture;
|
||||||
|
equirectMeshRenderer.sharedMaterial.mainTextureOffset = srcRectLeft.position;
|
||||||
|
equirectMeshRenderer.sharedMaterial.mainTextureScale = srcRectLeft.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Generate Mesh
|
||||||
|
equirectMeshFilter.mesh = CompositionLayer.MeshGenerationHelper.GenerateEquirectMesh(targetCompositionLayer.hmd, targetCompositionLayer.EquirectRadius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
}
|
||||||
if (Property_LayerShape.intValue == (int)CompositionLayer.LayerShape.Cylinder)
|
if (Property_LayerShape.intValue == (int)CompositionLayer.LayerShape.Cylinder)
|
||||||
{
|
{
|
||||||
if (!FeatureHelpers.GetFeatureWithIdForBuildTarget(BuildTargetGroup.Android, ViveCompositionLayerCylinder.featureId).enabled)
|
if (!FeatureHelpers.GetFeatureWithIdForBuildTarget(BuildTargetGroup.Android, ViveCompositionLayerCylinder.featureId).enabled)
|
||||||
@@ -174,6 +360,15 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetCompositionLayer.isPreviewingEquirect)
|
||||||
|
{
|
||||||
|
targetCompositionLayer.isPreviewingEquirect = false;
|
||||||
|
if (targetCompositionLayer.generatedPreview != null)
|
||||||
|
{
|
||||||
|
DestroyImmediate(targetCompositionLayer.generatedPreview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Transform generatedQuadTransform = targetCompositionLayer.transform.Find(CompositionLayer.QuadUnderlayMeshName);
|
Transform generatedQuadTransform = targetCompositionLayer.transform.Find(CompositionLayer.QuadUnderlayMeshName);
|
||||||
if (generatedQuadTransform != null)
|
if (generatedQuadTransform != null)
|
||||||
{
|
{
|
||||||
@@ -267,6 +462,7 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
|
|
||||||
if (targetCompositionLayer.isPreviewingCylinder)
|
if (targetCompositionLayer.isPreviewingCylinder)
|
||||||
{
|
{
|
||||||
|
|
||||||
Transform generatedPreviewTransform = targetCompositionLayer.transform.Find(CompositionLayer.CylinderPreviewName);
|
Transform generatedPreviewTransform = targetCompositionLayer.transform.Find(CompositionLayer.CylinderPreviewName);
|
||||||
|
|
||||||
if (generatedPreviewTransform != null)
|
if (generatedPreviewTransform != null)
|
||||||
@@ -311,6 +507,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
|
||||||
@@ -330,6 +532,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
|
||||||
@@ -351,6 +555,15 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetCompositionLayer.isPreviewingEquirect)
|
||||||
|
{
|
||||||
|
targetCompositionLayer.isPreviewingEquirect = false;
|
||||||
|
if (targetCompositionLayer.generatedPreview != null)
|
||||||
|
{
|
||||||
|
DestroyImmediate(targetCompositionLayer.generatedPreview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EditorGUI.indentLevel++;
|
EditorGUI.indentLevel++;
|
||||||
showLayerParams = EditorGUILayout.Foldout(showLayerParams, "Quad Parameters");
|
showLayerParams = EditorGUILayout.Foldout(showLayerParams, "Quad Parameters");
|
||||||
if (showLayerParams)
|
if (showLayerParams)
|
||||||
@@ -409,6 +622,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);
|
||||||
@@ -430,6 +649,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);
|
||||||
@@ -440,11 +661,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();
|
||||||
@@ -456,7 +684,7 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
//serializedObject.ApplyModifiedProperties();
|
//serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
//if (targetCompositionLayer.isExternalSurface)
|
//if (targetCompositionLayer.isExternalSurface)
|
||||||
if (false)
|
/*if (false)
|
||||||
{
|
{
|
||||||
EditorGUI.indentLevel++;
|
EditorGUI.indentLevel++;
|
||||||
showExternalSurfaceParams = EditorGUILayout.Foldout(showExternalSurfaceParams, "External Surface Parameters");
|
showExternalSurfaceParams = EditorGUILayout.Foldout(showExternalSurfaceParams, "External Surface Parameters");
|
||||||
@@ -469,8 +697,21 @@ namespace VIVE.OpenXR.CompositionLayer.Editor
|
|||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
}
|
}
|
||||||
EditorGUI.indentLevel--;
|
EditorGUI.indentLevel--;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if((Property_LayerShape.intValue != (int)CompositionLayer.LayerShape.Equirect && Property_LayerShape.intValue != (int)CompositionLayer.LayerShape.Equirect2) && (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();
|
||||||
|
|
||||||
|
|||||||
166
com.htc.upm.vive.openxr/Editor/PackageManagerHelper.cs
Normal file
166
com.htc.upm.vive.openxr/Editor/PackageManagerHelper.cs
Normal 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)
|
||||||
|
{
|
||||||
|
if (m_removeRequest != null) { Debug.LogError("Something wrong when removing package from list. error:" + m_removeRequest.Error.errorCode + "(" + m_removeRequest.Error.message + ")"); }
|
||||||
|
var request = m_removeRequest;
|
||||||
|
m_removeRequest = null;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: a00aae5e5e4790c429467d7a03b4a6de
|
guid: 43952515f295bac4385e71851692047d
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: ba676113e4d2dfc4095675f6fb934d9e
|
guid: 5f1198e3724eb5b44a705edc6d6bae06
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -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
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: b048c9b388bf8d34e9814b272da7ddb5
|
guid: 94f6766384418a0418eb5ebdb371be20
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
// 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 int checkPreferenceAssetsFrame = 0;
|
||||||
|
static void OnUpdate()
|
||||||
|
{
|
||||||
|
if (!ViveOpenXRAndroidAssigned) { return; }
|
||||||
|
|
||||||
|
checkPreferenceAssetsFrame++;
|
||||||
|
checkPreferenceAssetsFrame %= 1200; // 10s
|
||||||
|
if (checkPreferenceAssetsFrame != 0) { 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
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 484659e19359fb740ac4a253c3fa83c6
|
guid: 73bdd0b88ffae0e43a3a498347e6dea4
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -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
|
|
||||||
24
com.htc.upm.vive.openxr/Editor/ViveAnchorEditor.cs
Normal file
24
com.htc.upm.vive.openxr/Editor/ViveAnchorEditor.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 6acba111d20a438439e5d1152010efa5
|
guid: 9094698271e2abb4ab295256548772c3
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
44
com.htc.upm.vive.openxr/Editor/ViveMenu.cs
Normal file
44
com.htc.upm.vive.openxr/Editor/ViveMenu.cs
Normal 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
|
||||||
11
com.htc.upm.vive.openxr/Editor/ViveMenu.cs.meta
Normal file
11
com.htc.upm.vive.openxr/Editor/ViveMenu.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0f78968df8bc5794393fb2016e223a6c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
64
com.htc.upm.vive.openxr/Editor/ViveMockRuntimeEditor.cs
Normal file
64
com.htc.upm.vive.openxr/Editor/ViveMockRuntimeEditor.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
com.htc.upm.vive.openxr/Editor/ViveMockRuntimeEditor.cs.meta
Normal file
11
com.htc.upm.vive.openxr/Editor/ViveMockRuntimeEditor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: eedb4211aafd2cb4bae86fcc0e948f72
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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
|
|
||||||
@@ -14,19 +14,33 @@ namespace VIVE.OpenXR
|
|||||||
"vive.openxr.feature.compositionlayer",
|
"vive.openxr.feature.compositionlayer",
|
||||||
"vive.openxr.feature.compositionlayer.cylinder",
|
"vive.openxr.feature.compositionlayer.cylinder",
|
||||||
"vive.openxr.feature.compositionlayer.colorscalebias",
|
"vive.openxr.feature.compositionlayer.colorscalebias",
|
||||||
|
CompositionLayer.ViveCompositionLayerEquirect.featureId,
|
||||||
Tracker.ViveWristTracker.featureId,
|
Tracker.ViveWristTracker.featureId,
|
||||||
Hand.ViveHandInteraction.featureId,
|
Hand.ViveHandInteraction.featureId,
|
||||||
"vive.openxr.feature.foveation",
|
"vive.openxr.feature.foveation",
|
||||||
FacialTracking.ViveFacialTracking.featureId,
|
FacialTracking.ViveFacialTracking.featureId,
|
||||||
PlaneDetection.VivePlaneDetection.featureId,
|
PlaneDetection.VivePlaneDetection.featureId,
|
||||||
Anchor.ViveAnchor.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",
|
UiName = "VIVE XR Support",
|
||||||
Description = "Necessary to deploy an VIVE XR compatible app.",
|
Description = "Necessary to deploy an VIVE XR compatible app.",
|
||||||
FeatureSetId = "com.htc.vive.openxr.featureset.vivexr",
|
FeatureSetId = "com.htc.vive.openxr.featureset.vivexr",
|
||||||
|
#if UNITY_ANDROID
|
||||||
DefaultFeatureIds = new string[] { VIVEFocus3Feature.featureId, VIVEFocus3Profile.featureId, },
|
DefaultFeatureIds = new string[] { VIVEFocus3Feature.featureId, VIVEFocus3Profile.featureId, },
|
||||||
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.Android }
|
#endif
|
||||||
|
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone }
|
||||||
)]
|
)]
|
||||||
sealed class VIVEFocus3FeatureSet { }
|
sealed class ViveOpenXRFeatureSet { }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
81
com.htc.upm.vive.openxr/Editor/ViveSpectatorCameraProcess.cs
Normal file
81
com.htc.upm.vive.openxr/Editor/ViveSpectatorCameraProcess.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4fcb7e5a984acb64bb9221b9b05c0517
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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.
|
|
||||||
|
|
||||||
|
|
||||||
4
com.htc.upm.vive.openxr/LICENSE.txt
Normal file
4
com.htc.upm.vive.openxr/LICENSE.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Copyright © HTC Corporation, LLC and its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
Your use of this SDK, sample, or tool is subject to HTC VIVE SDK License Agreement, available at https://developer.vive.com/resources/downloads/licenses-and-agreements/
|
||||||
|
|
||||||
BIN
com.htc.upm.vive.openxr/MockRuntime~/Win64/ViveMockRuntime.dll
LFS
Normal file
BIN
com.htc.upm.vive.openxr/MockRuntime~/Win64/ViveMockRuntime.dll
LFS
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.Feature
|
||||||
|
{
|
||||||
|
public interface IViveFeatureWrapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// OnInstanceCreate might be called multiple times. Because many features might be using the same instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xrInstance"></param>
|
||||||
|
/// <param name="xrGetInstanceProcAddr"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr xrGetInstanceProcAddr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OnInstanceDestroy might be called multiple times. Because many features might be using the same instance.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the feature is inited not successfully, Set this true. Use to avoid multiple inits.
|
||||||
|
/// </summary>
|
||||||
|
public bool TryInited { 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a27dc5505cdb29347aeda46676cedaa8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
228
com.htc.upm.vive.openxr/Runtime/Common/MemoryTools.cs
Normal file
228
com.htc.upm.vive.openxr/Runtime/Common/MemoryTools.cs
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
internal static class MemoryTools
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Make sure the input ptr is a OpenXR XrBaseStructure derived struct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">the struct to get its next.</param>
|
||||||
|
/// <returns>the next's value</returns>
|
||||||
|
public static unsafe IntPtr GetNext(IntPtr ptr)
|
||||||
|
{
|
||||||
|
if (ptr == IntPtr.Zero)
|
||||||
|
return IntPtr.Zero;
|
||||||
|
//Profiler.BeginSample("GetNext");
|
||||||
|
XrBaseStructure* ptrToStruct = (XrBaseStructure*)ptr.ToPointer();
|
||||||
|
//Profiler.EndSample();
|
||||||
|
return ptrToStruct->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make sure the input ptr is a OpenXR XrBaseStructure derived struct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">the struct to get its type</param>
|
||||||
|
/// <returns>the struct's type</returns>
|
||||||
|
public static unsafe XrStructureType GetType(IntPtr ptr)
|
||||||
|
{
|
||||||
|
if (ptr == IntPtr.Zero)
|
||||||
|
throw new Exception("The input pointer is null.");
|
||||||
|
|
||||||
|
//Profiler.BeginSample("GetType");
|
||||||
|
XrBaseStructure* ptrToStruct = (XrBaseStructure*)ptr.ToPointer();
|
||||||
|
//Profiler.EndSample();
|
||||||
|
return ptrToStruct->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe XrBaseStructure ToBaseStructure(IntPtr ptr)
|
||||||
|
{
|
||||||
|
if (ptr == IntPtr.Zero)
|
||||||
|
throw new Exception("The input pointer is null.");
|
||||||
|
|
||||||
|
//Profiler.BeginSample("ToBaseStructure");
|
||||||
|
XrBaseStructure* ptrToStruct = (XrBaseStructure*)ptr.ToPointer();
|
||||||
|
//Profiler.EndSample();
|
||||||
|
return *ptrToStruct;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe T PtrToStructure<T>(IntPtr ptr) where T : unmanaged
|
||||||
|
{
|
||||||
|
//Profiler.BeginSample("PtrToStructure");
|
||||||
|
// Not to use Marshal.PtrToStructure<T> because it is slow.
|
||||||
|
T t = default; // Use new T() will cause GC alloc.
|
||||||
|
Buffer.MemoryCopy((void*)ptr, &t, sizeof(T), sizeof(T));
|
||||||
|
//Profiler.EndSample();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe void PtrToStructure<T>(IntPtr ptr, ref T t) where T : unmanaged
|
||||||
|
{
|
||||||
|
//Profiler.BeginSample("PtrToStructure");
|
||||||
|
fixed (T* destinationPtr = &t)
|
||||||
|
{
|
||||||
|
Buffer.MemoryCopy((void*)ptr, destinationPtr, sizeof(T), sizeof(T));
|
||||||
|
}
|
||||||
|
//Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe void StructureToPtr<T>(T t, IntPtr ptr) where T : unmanaged
|
||||||
|
{
|
||||||
|
//Profiler.BeginSample("StructureToPtr");
|
||||||
|
// Not to use Marshal.StructureToPtr<T> because it is slow.
|
||||||
|
Buffer.MemoryCopy(&t, (void*)ptr, sizeof(T), sizeof(T));
|
||||||
|
//Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the enum array to IntPtr. Should call <see cref="ReleaseRawMemory(IntPtr)"/> after use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static unsafe IntPtr ToIntPtr<T>(T[] array) where T : Enum
|
||||||
|
{
|
||||||
|
int size = sizeof(int) * array.Length;
|
||||||
|
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||||
|
|
||||||
|
int* intPtr = (int*)ptr.ToPointer();
|
||||||
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
// Convert enum to int. This has better performance than Convert.ToInt32.
|
||||||
|
intPtr[i] = (int)(object)array[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the struct to IntPtr. Should call <see cref="ReleaseRawMemory(IntPtr)"/> after use.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="structure"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IntPtr ToIntPtr<T>(T structure) where T : struct
|
||||||
|
{
|
||||||
|
int size = Marshal.SizeOf(structure);
|
||||||
|
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||||
|
Marshal.StructureToPtr(structure, ptr, true);
|
||||||
|
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 unsafe IntPtr MakeRawMemory<T>(T[] refArray) where T : unmanaged
|
||||||
|
{
|
||||||
|
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 unsafe void CopyFromRawMemory<T>(T[] array, IntPtr raw, int count = 0) where T : unmanaged
|
||||||
|
{
|
||||||
|
//Profiler.BeginSample("CopyFromRawMemory");
|
||||||
|
int N = array.Length;
|
||||||
|
if (count > 0 && count < array.Length)
|
||||||
|
N = count;
|
||||||
|
int step = sizeof(T);
|
||||||
|
int bufferSize = step * N;
|
||||||
|
|
||||||
|
// Pin array's address. Prevent GC move it.
|
||||||
|
fixed (T* destPtr = array)
|
||||||
|
{
|
||||||
|
T* sourcePtr = (T*)raw.ToPointer();
|
||||||
|
Buffer.MemoryCopy(sourcePtr, destPtr, bufferSize, bufferSize);
|
||||||
|
}
|
||||||
|
//Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy all raw memory to the array. This has higher performance than <see cref="CopyFromRawMemory"/>.
|
||||||
|
/// Use this method if you have frequent update requirements.
|
||||||
|
/// You need prepare a byte buffer to store the raw memory. The byte buffer size should be tSize * array.Length.
|
||||||
|
/// tSize is used for checking the byte buffer size. If tSize is 0, it will use Marshal.SizeOf(typeof(T)).
|
||||||
|
/// You can save the size at your size to avoid the Marshal.Sizeof(typeof(T)) call repeatedly.
|
||||||
|
/// </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>
|
||||||
|
public static unsafe void CopyAllFromRawMemory<T>(T[] array, IntPtr raw) where T : unmanaged
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (array == null)
|
||||||
|
throw new ArgumentNullException(nameof(array), "Output array cannot be null.");
|
||||||
|
if (raw == IntPtr.Zero)
|
||||||
|
throw new ArgumentNullException(nameof(raw), "Raw memory pointer cannot be null.");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//Profiler.BeginSample("CopyAllFromRawMemory");
|
||||||
|
int elementSize = sizeof(T);
|
||||||
|
int requiredBufferSize = elementSize * array.Length;
|
||||||
|
|
||||||
|
// Pin array's address. Prevent GC move it.
|
||||||
|
fixed (T* destPtr = array)
|
||||||
|
{
|
||||||
|
T* sourcePtr = (T*)raw.ToPointer();
|
||||||
|
Buffer.MemoryCopy(sourcePtr, destPtr, requiredBufferSize, requiredBufferSize);
|
||||||
|
}
|
||||||
|
//Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 unsafe void CopyToRawMemory<T>(IntPtr raw, T[] array) where T : unmanaged
|
||||||
|
{
|
||||||
|
//Profiler.BeginSample("CopyToRawMemory");
|
||||||
|
int step = sizeof(T);
|
||||||
|
int bufferSize = step * array.Length;
|
||||||
|
// Pin array's address. Prevent GC move it.
|
||||||
|
fixed (T* destPtr = array)
|
||||||
|
{
|
||||||
|
void* ptr = raw.ToPointer();
|
||||||
|
Buffer.MemoryCopy(destPtr, ptr, bufferSize, bufferSize);
|
||||||
|
}
|
||||||
|
//Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find a pointer in the next chain. Make sure the input next pointer is a OpenXR XrBaseStructure derived struct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target"></param>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <returns>true if exist</returns>
|
||||||
|
public static bool HasPtrInNextChain(IntPtr target, IntPtr next)
|
||||||
|
{
|
||||||
|
while (next != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
if (next == target)
|
||||||
|
return true;
|
||||||
|
next = GetNext(next);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
278
com.htc.upm.vive.openxr/Runtime/Common/ViveEnterpriseCommand.cs
Normal file
278
com.htc.upm.vive.openxr/Runtime/Common/ViveEnterpriseCommand.cs
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
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.Enterprise
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[OpenXRFeature(UiName = "VIVE XR Enterprise Command",
|
||||||
|
Desc = "Support Enterprise request with special command",
|
||||||
|
Company = "HTC",
|
||||||
|
OpenxrExtensionStrings = kOpenxrExtensionString,
|
||||||
|
Version = "0.1",
|
||||||
|
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||||
|
FeatureId = featureId,
|
||||||
|
Hidden = true
|
||||||
|
)]
|
||||||
|
#endif
|
||||||
|
public class ViveEnterpriseCommand : OpenXRFeature
|
||||||
|
{
|
||||||
|
#region Log
|
||||||
|
const string LOG_TAG = "VIVE.OpenXR.Enterprise.Command ";
|
||||||
|
private static void DEBUG(String msg) { Debug.Log(LOG_TAG + msg); }
|
||||||
|
private static void ERROR(String msg) { Debug.LogError(LOG_TAG + msg); }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||||
|
/// </summary>
|
||||||
|
public const string featureId = "vive.openxr.feature.enterprise.command";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The extension string.
|
||||||
|
/// </summary>
|
||||||
|
public const string kOpenxrExtensionString = "XR_HTC_enterprise_command";
|
||||||
|
|
||||||
|
#region OpenXR Life Cycle
|
||||||
|
private static bool m_XrInstanceCreated = false;
|
||||||
|
private static bool m_XrSessionCreated = false;
|
||||||
|
private static XrInstance m_XrInstance = 0;
|
||||||
|
private static XrSession m_XrSession = 0;
|
||||||
|
private static XrSystemId m_XrSystemId = 0;
|
||||||
|
|
||||||
|
/// <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))
|
||||||
|
{
|
||||||
|
ERROR($"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)
|
||||||
|
{
|
||||||
|
if (m_XrInstance == xrInstance)
|
||||||
|
{
|
||||||
|
m_XrInstanceCreated = false;
|
||||||
|
m_XrInstance = 0;
|
||||||
|
}
|
||||||
|
DEBUG($"OnInstanceDestroy() {xrInstance}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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}");
|
||||||
|
|
||||||
|
if (m_XrSession == xrSession)
|
||||||
|
{
|
||||||
|
m_XrSession = 0;
|
||||||
|
m_XrSessionCreated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region OpenXR function delegates
|
||||||
|
/// xrEnterpriseCommandHTC
|
||||||
|
private static ViveEnterpriseCommandHelper.xrEnterpriseCommandHTCDelegate xrEnterpriseCommandHTC;
|
||||||
|
/// xrGetInstanceProcAddr
|
||||||
|
private static OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enterprise command request for special functionality.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The request of enterprise command</param>
|
||||||
|
/// <param name="result">The result of enterprise command</param>
|
||||||
|
/// <returns>Return XR_SUCCESS if request successfully. False otherwise.</returns>
|
||||||
|
private static XrResult EnterpriseCommandHTC(XrEnterpriseCommandBufferHTC request, ref XrEnterpriseCommandBufferHTC result)
|
||||||
|
{
|
||||||
|
if (!m_XrSessionCreated)
|
||||||
|
{
|
||||||
|
ERROR("EnterpriseCommandHTC() XR_ERROR_SESSION_LOST.");
|
||||||
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
}
|
||||||
|
if (!m_XrInstanceCreated)
|
||||||
|
{
|
||||||
|
ERROR("EnterpriseCommandHTC() XR_ERROR_INSTANCE_LOST.");
|
||||||
|
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG($"EnterpriseCommandHTC() code: {request.code}, data: {CharArrayToString(request.data)}");
|
||||||
|
return xrEnterpriseCommandHTC(m_XrSession, request, ref result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 request successfully. False otherwise.</returns>
|
||||||
|
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");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xrEnterpriseCommandHTC
|
||||||
|
if (XrGetInstanceProcAddr(xrInstance, "xrEnterpriseCommandHTC", out IntPtr funcPtr) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (funcPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DEBUG("Get function pointer of xrEnterpriseCommandHTC.");
|
||||||
|
xrEnterpriseCommandHTC = Marshal.GetDelegateForFunctionPointer(
|
||||||
|
funcPtr,
|
||||||
|
typeof(ViveEnterpriseCommandHelper.xrEnterpriseCommandHTCDelegate)) as ViveEnterpriseCommandHelper.xrEnterpriseCommandHTCDelegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR("xrEnterpriseCommandHTC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public API
|
||||||
|
private const int kCharLength = 256;
|
||||||
|
private const char kEndChar = '\0';
|
||||||
|
private static char[] charArray = new char[kCharLength];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request special feature with command, it should take code and command string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="requestCode">The type of request code is integer.</param>
|
||||||
|
/// <param name="requestCommand">The maximum length of request command is 256.</param>
|
||||||
|
/// <param name="resultCode">The output of result code.</param>
|
||||||
|
/// <param name="resultCommand">The output of result command.</param>
|
||||||
|
/// <returns>Return true if request successfully. False otherwise.</returns>
|
||||||
|
public static bool CommandRequest(int requestCode, string requestCommand, out int resultCode, out string resultCommand)
|
||||||
|
{
|
||||||
|
resultCode = 0;
|
||||||
|
resultCommand = string.Empty;
|
||||||
|
XrEnterpriseCommandBufferHTC request = new XrEnterpriseCommandBufferHTC(requestCode, StringToCharArray(requestCommand));
|
||||||
|
XrEnterpriseCommandBufferHTC result = new XrEnterpriseCommandBufferHTC(resultCode, StringToCharArray(resultCommand));
|
||||||
|
if (EnterpriseCommandHTC(request, ref result) == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
|
resultCode = result.code;
|
||||||
|
resultCommand = CharArrayToString(result.data);
|
||||||
|
DEBUG($"CommandRequest Result code: {resultCode}, data: {resultCommand}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private static char[] StringToCharArray(string str)
|
||||||
|
{
|
||||||
|
Array.Clear(charArray, 0, kCharLength);
|
||||||
|
if (!string.IsNullOrEmpty(str))
|
||||||
|
{
|
||||||
|
int arrayLength = Math.Min(str.Length, kCharLength);
|
||||||
|
for (int i = 0; i < arrayLength; i++)
|
||||||
|
{
|
||||||
|
charArray[i] = str[i];
|
||||||
|
}
|
||||||
|
charArray[kCharLength - 1] = kEndChar;
|
||||||
|
}
|
||||||
|
return charArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CharArrayToString(char[] charArray)
|
||||||
|
{
|
||||||
|
int actualLength = Array.FindIndex(charArray, c => c == kEndChar);
|
||||||
|
if (actualLength == -1)
|
||||||
|
{
|
||||||
|
actualLength = charArray.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new string(charArray, 0, actualLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Helper
|
||||||
|
public struct XrEnterpriseCommandBufferHTC
|
||||||
|
{
|
||||||
|
public Int32 code;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
|
||||||
|
public char[] data;
|
||||||
|
|
||||||
|
public XrEnterpriseCommandBufferHTC(int in_code, char[] in_data)
|
||||||
|
{
|
||||||
|
code = (Int32)in_code;
|
||||||
|
data = new char[in_data.Length];
|
||||||
|
Array.Copy(in_data, data, in_data.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViveEnterpriseCommandHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The function delegate of xrEnterpriseCommandHTC.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="session">An <see cref="XrSession">XrSession</see> in which the enterprise command will be active.</param>
|
||||||
|
/// <param name="request">The request of enterprise command</param>
|
||||||
|
/// <param name="result">The result of enterprise command</param>
|
||||||
|
/// <returns>Return XR_SUCCESS if request successfully. False otherwise.</returns>
|
||||||
|
public delegate XrResult xrEnterpriseCommandHTCDelegate(
|
||||||
|
XrSession session,
|
||||||
|
XrEnterpriseCommandBufferHTC request,
|
||||||
|
ref XrEnterpriseCommandBufferHTC result);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d5b2125d5dc73694eaed34e97a0962e0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -3,30 +3,29 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
|
||||||
namespace VIVE.OpenXR.Feature
|
namespace VIVE.OpenXR.Feature
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// To use this wrapper, you need to call CommonWrapper.Instance.OnInstanceCreate() in your feature's OnInstanceCreate(),
|
/// 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().
|
/// and call CommonWrapper.Instance.OnInstanceDestroy() in your feature's OnInstanceDestroy().
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// In Standardalone's OpenXR MockRuntime, the CreateSwapchain and EnumerateSwapchainImages will work and return success,
|
||||||
|
/// but the images's native pointer will be null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CommonWrapper
|
internal class CommonWrapper : ViveFeatureWrapperBase<CommonWrapper>, IViveFeatureWrapper
|
||||||
{
|
{
|
||||||
static CommonWrapper instance = null;
|
const string TAG = "CommonWrapper";
|
||||||
public static CommonWrapper Instance
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (instance == null)
|
|
||||||
instance = new CommonWrapper();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInited = false;
|
|
||||||
|
|
||||||
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
|
||||||
OpenXRHelper.xrGetSystemPropertiesDelegate XrGetSystemProperties;
|
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>
|
/// <summary>
|
||||||
/// In feature's OnInstanceCreate(), call CommonWrapper.Instance.OnInstanceCreate() for init common APIs.
|
/// In feature's OnInstanceCreate(), call CommonWrapper.Instance.OnInstanceCreate() for init common APIs.
|
||||||
@@ -35,32 +34,34 @@ namespace VIVE.OpenXR.Feature
|
|||||||
/// <param name="xrGetInstanceProcAddr">Pass OpenXRFeature.xrGetInstanceProcAddr in.</param>
|
/// <param name="xrGetInstanceProcAddr">Pass OpenXRFeature.xrGetInstanceProcAddr in.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="Exception">If input data not valid.</exception>
|
/// <exception cref="Exception">If input data not valid.</exception>
|
||||||
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr xrGetInstanceProcAddr)
|
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrPtr)
|
||||||
{
|
{
|
||||||
if (isInited) return true;
|
if (IsInited) return true;
|
||||||
|
if (TryInited) return false;
|
||||||
|
TryInited = true;
|
||||||
|
|
||||||
if (xrInstance == 0)
|
if (xrInstance == 0)
|
||||||
throw new Exception("CommonWrapper: xrInstance is null");
|
throw new Exception("CommonWrapper: xrInstance is null");
|
||||||
|
|
||||||
Debug.Log("CommonWrapper: OnInstanceCreate()");
|
Log.D(TAG, "OnInstanceCreate()");
|
||||||
/// OpenXRFeature.xrGetInstanceProcAddr
|
SetGetInstanceProcAddrPtr(xrGetInstanceProcAddrPtr);
|
||||||
if (xrGetInstanceProcAddr == null || xrGetInstanceProcAddr == IntPtr.Zero)
|
|
||||||
throw new Exception("CommonWrapper: xrGetInstanceProcAddr is null");
|
|
||||||
|
|
||||||
Debug.Log("CommonWrapper: Get function pointer of xrGetInstanceProcAddr.");
|
|
||||||
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
|
|
||||||
xrGetInstanceProcAddr,
|
|
||||||
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
|
|
||||||
|
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
IntPtr funcPtr = IntPtr.Zero;
|
IntPtr funcPtr = IntPtr.Zero;
|
||||||
|
|
||||||
ret &= OpenXRHelper.GetXrFunctionDelegate(XrGetInstanceProcAddr, xrInstance, "xrGetSystemProperties", out XrGetSystemProperties);
|
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)
|
if (!ret)
|
||||||
throw new Exception("CommonWrapper: Get function pointers failed.");
|
throw new Exception("CommonWrapper: Get function pointers failed.");
|
||||||
|
|
||||||
isInited = ret;
|
IsInited = ret;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,21 +71,22 @@ namespace VIVE.OpenXR.Feature
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public void OnInstanceDestroy()
|
public void OnInstanceDestroy()
|
||||||
{
|
{
|
||||||
isInited = false;
|
// Do not destroy twice
|
||||||
XrGetInstanceProcAddr = null;
|
if (IsInited == false) return;
|
||||||
|
IsInited = false;
|
||||||
XrGetSystemProperties = null;
|
XrGetSystemProperties = null;
|
||||||
Debug.Log("CommonWrapper: OnInstanceDestroy()");
|
Log.D(TAG, "OnInstanceDestroy()");
|
||||||
}
|
}
|
||||||
|
|
||||||
public XrResult GetInstanceProcAddr(XrInstance instance, string name, out IntPtr function)
|
public XrResult GetInstanceProcAddr(XrInstance instance, string name, out IntPtr function)
|
||||||
{
|
{
|
||||||
if (isInited == false || XrGetInstanceProcAddr == null)
|
if (IsInited == false || xrGetInstanceProcAddr == null)
|
||||||
{
|
{
|
||||||
function = IntPtr.Zero;
|
function = IntPtr.Zero;
|
||||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
return XrGetInstanceProcAddr(instance, name, out function);
|
return xrGetInstanceProcAddr(instance, name, out function);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -97,7 +99,7 @@ namespace VIVE.OpenXR.Feature
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public XrResult GetSystemProperties(XrInstance instance, XrSystemId systemId, ref XrSystemProperties properties)
|
public XrResult GetSystemProperties(XrInstance instance, XrSystemId systemId, ref XrSystemProperties properties)
|
||||||
{
|
{
|
||||||
if (isInited == false || XrGetSystemProperties == null)
|
if (IsInited == false || XrGetSystemProperties == null)
|
||||||
{
|
{
|
||||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||||
}
|
}
|
||||||
@@ -136,5 +138,115 @@ namespace VIVE.OpenXR.Feature
|
|||||||
Marshal.FreeHGlobal(systemProperties.next);
|
Marshal.FreeHGlobal(systemProperties.next);
|
||||||
return ret;
|
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)
|
||||||
|
{
|
||||||
|
Log.D(TAG, "EnumerateSwapchainFormats(ci=" + formatCapacityInput + ")");
|
||||||
|
return XrEnumerateSwapchainFormats(session, 0, ref formatCountOutput, IntPtr.Zero);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.D(TAG, "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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,211 @@
|
|||||||
|
// 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
|
||||||
|
{
|
||||||
|
const string TAG = "ViveFuture";
|
||||||
|
|
||||||
|
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 (TryInited) return false;
|
||||||
|
TryInited = 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);
|
||||||
|
|
||||||
|
Log.D(TAG, "OnInstanceCreate()");
|
||||||
|
|
||||||
|
bool hasFuture = OpenXRRuntime.IsExtensionEnabled("XR_EXT_future");
|
||||||
|
if (!hasFuture)
|
||||||
|
{
|
||||||
|
Log.E(TAG, "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)
|
||||||
|
{
|
||||||
|
Log.E(TAG,"FutureWrapper: Failed to get function pointer.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsInited = ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnInstanceDestroy()
|
||||||
|
{
|
||||||
|
Log.D(TAG, "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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e8522c7af0a4127409a8800e1ddd5985
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
// Copyright HTC Corporation All Rights Reserved.
|
// 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;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace VIVE.OpenXR.Feature
|
namespace VIVE.OpenXR.Feature
|
||||||
@@ -15,24 +11,15 @@ namespace VIVE.OpenXR.Feature
|
|||||||
/// To use this wrapper, you need to call CommonWrapper.Instance.OnInstanceCreate() in your feature's OnInstanceCreate(),
|
/// 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().
|
/// and call CommonWrapper.Instance.OnInstanceDestroy() in your feature's OnInstanceDestroy().
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SpaceWrapper
|
public class SpaceWrapper : ViveFeatureWrapperBase<SpaceWrapper>, IViveFeatureWrapper
|
||||||
{
|
{
|
||||||
static SpaceWrapper instance = null;
|
const string TAG = "ViveSpaceWrapper";
|
||||||
public static SpaceWrapper Instance
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (instance == null)
|
|
||||||
instance = new SpaceWrapper();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInited = false;
|
|
||||||
|
|
||||||
|
public delegate XrResult DelegateXrEnumerateReferenceSpaces(XrSession session, uint spaceCapacityInput, out uint spaceCountOutput, [Out] XrReferenceSpaceType[] spaces);
|
||||||
delegate XrResult DelegateXrLocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location);
|
delegate XrResult DelegateXrLocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location);
|
||||||
delegate XrResult DelegateXrDestroySpace(XrSpace space);
|
delegate XrResult DelegateXrDestroySpace(XrSpace space);
|
||||||
|
|
||||||
|
DelegateXrEnumerateReferenceSpaces XrEnumerateReferenceSpaces;
|
||||||
OpenXRHelper.xrCreateReferenceSpaceDelegate XrCreateReferenceSpace;
|
OpenXRHelper.xrCreateReferenceSpaceDelegate XrCreateReferenceSpace;
|
||||||
DelegateXrLocateSpace XrLocateSpace;
|
DelegateXrLocateSpace XrLocateSpace;
|
||||||
DelegateXrDestroySpace XrDestroySpace;
|
DelegateXrDestroySpace XrDestroySpace;
|
||||||
@@ -44,41 +31,66 @@ namespace VIVE.OpenXR.Feature
|
|||||||
/// <param name="GetAddr"></param>
|
/// <param name="GetAddr"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="Exception"></exception>
|
/// <exception cref="Exception"></exception>
|
||||||
public bool OnInstanceCreate(XrInstance xrInstance, OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr)
|
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr GetAddr)
|
||||||
{
|
{
|
||||||
if (isInited) return true;
|
if (IsInited) return true;
|
||||||
|
if (TryInited) return false;
|
||||||
|
TryInited = true;
|
||||||
|
|
||||||
if (xrInstance == null)
|
if (xrInstance == null)
|
||||||
throw new Exception("ViveSpace: xrInstance is null");
|
throw new Exception("ViveSpaceWrapper: xrInstance is null");
|
||||||
|
|
||||||
if (GetAddr == null)
|
SetGetInstanceProcAddrPtr(GetAddr);
|
||||||
throw new Exception("ViveSpace: xrGetInstanceProcAddr is null");
|
|
||||||
|
|
||||||
Debug.Log("ViveSpace: OnInstanceCreate()");
|
Log.D(TAG, "OnInstanceCreate()");
|
||||||
|
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
IntPtr funcPtr = IntPtr.Zero;
|
IntPtr funcPtr = IntPtr.Zero;
|
||||||
|
|
||||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrCreateReferenceSpace", out XrCreateReferenceSpace);
|
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrEnumerateReferenceSpaces", out XrEnumerateReferenceSpaces);
|
||||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrLocateSpace", out XrLocateSpace);
|
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrCreateReferenceSpace", out XrCreateReferenceSpace);
|
||||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrDestroySpace", out XrDestroySpace);
|
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrLocateSpace", out XrLocateSpace);
|
||||||
isInited = ret;
|
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrDestroySpace", out XrDestroySpace);
|
||||||
|
IsInited = ret;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnInstanceDestroy()
|
public void OnInstanceDestroy()
|
||||||
{
|
{
|
||||||
isInited = false;
|
// Do not destroy twice
|
||||||
|
if (IsInited == false) return;
|
||||||
|
IsInited = false;
|
||||||
|
XrEnumerateReferenceSpaces = null;
|
||||||
XrCreateReferenceSpace = null;
|
XrCreateReferenceSpace = null;
|
||||||
XrLocateSpace = null;
|
XrLocateSpace = null;
|
||||||
XrDestroySpace = null;
|
XrDestroySpace = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="session"></param>
|
||||||
|
/// <param name="spaceCapacityInput"></param>
|
||||||
|
/// <param name="spaceCountOutput"></param>
|
||||||
|
/// <param name="spaces"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public XrResult EnumerateReferenceSpaces(XrSession session, int spaceCapacityInput, ref int spaceCountOutput, ref XrReferenceSpaceType[] spaces)
|
||||||
|
{
|
||||||
|
spaceCountOutput = 0;
|
||||||
|
if (!IsInited)
|
||||||
|
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||||
|
if (spaceCapacityInput != 0 && spaces != null && spaces.Length < spaceCapacityInput)
|
||||||
|
return XrResult.XR_ERROR_SIZE_INSUFFICIENT;
|
||||||
|
var ret = XrEnumerateReferenceSpaces(session, (uint)spaceCapacityInput, out uint spaceCountOutputXR, spaces);
|
||||||
|
spaceCountOutput = (int)spaceCountOutputXR;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a reference space without create info.
|
/// Create a reference space without create info.
|
||||||
/// Example:
|
/// Example:
|
||||||
/// CreateReferenceSpace(session, XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_LOCAL, XrPosef.identity, out space);
|
/// CreateReferenceSpace(session, XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_LOCAL, XrPosef.Identity, out space);
|
||||||
/// CreateReferenceSpace(session, XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_STAGE, XrPosef.identity, out space);
|
/// CreateReferenceSpace(session, XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_STAGE, XrPosef.Identity, out space);
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session"></param>
|
/// <param name="session"></param>
|
||||||
/// <param name="referenceSpaceType"></param>
|
/// <param name="referenceSpaceType"></param>
|
||||||
@@ -87,8 +99,9 @@ namespace VIVE.OpenXR.Feature
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public XrResult CreateReferenceSpace(XrSession session, XrReferenceSpaceType referenceSpaceType, XrPosef pose, out XrSpace space)
|
public XrResult CreateReferenceSpace(XrSession session, XrReferenceSpaceType referenceSpaceType, XrPosef pose, out XrSpace space)
|
||||||
{
|
{
|
||||||
if (!isInited)
|
space = 0;
|
||||||
throw new Exception("ViveSpace: not initialized");
|
if (!IsInited)
|
||||||
|
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||||
|
|
||||||
var createInfo = new XrReferenceSpaceCreateInfo();
|
var createInfo = new XrReferenceSpaceCreateInfo();
|
||||||
createInfo.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
createInfo.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
||||||
@@ -107,25 +120,26 @@ namespace VIVE.OpenXR.Feature
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public XrResult CreateReferenceSpace(XrSession session, XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
public XrResult CreateReferenceSpace(XrSession session, XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
||||||
{
|
{
|
||||||
if (!isInited)
|
space = 0;
|
||||||
throw new Exception("ViveSpace: not initialized");
|
if (!IsInited)
|
||||||
|
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||||
|
|
||||||
return XrCreateReferenceSpace(session, ref createInfo, out space);
|
return XrCreateReferenceSpace(session, ref createInfo, out space);
|
||||||
}
|
}
|
||||||
|
|
||||||
public XrResult LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location)
|
public XrResult LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location)
|
||||||
{
|
{
|
||||||
if (!isInited)
|
if (!IsInited)
|
||||||
throw new Exception("ViveSpace: not initialized");
|
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||||
Debug.Log($"LocateSpace(s={space}, bs={baseSpace}, t={time}");
|
//Debug.Log($"LocateSpace(s={space}, bs={baseSpace}, t={time}");
|
||||||
return XrLocateSpace(space, baseSpace, time, ref location);
|
return XrLocateSpace(space, baseSpace, time, ref location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public XrResult DestroySpace(XrSpace space)
|
public XrResult DestroySpace(XrSpace space)
|
||||||
{
|
{
|
||||||
if (!isInited)
|
if (!IsInited)
|
||||||
throw new Exception("ViveSpace: not initialized");
|
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||||
Debug.Log($"DestroySpace({space})");
|
Log.D(TAG, $"DestroySpace({space})");
|
||||||
return XrDestroySpace(space);
|
return XrDestroySpace(space);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,7 +155,7 @@ namespace VIVE.OpenXR.Feature
|
|||||||
|
|
||||||
public Space(XrSpace space)
|
public Space(XrSpace space)
|
||||||
{
|
{
|
||||||
Debug.Log($"Space({space})");
|
Log.D($"Space({space})");
|
||||||
this.space = space;
|
this.space = space;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,19 +171,6 @@ namespace VIVE.OpenXR.Feature
|
|||||||
|
|
||||||
public bool GetRelatedPose(XrSpace baseSpace, XrTime time, out UnityEngine.Pose pose)
|
public bool GetRelatedPose(XrSpace baseSpace, XrTime time, out UnityEngine.Pose pose)
|
||||||
{
|
{
|
||||||
#if FAKE_DATA
|
|
||||||
if (Application.isEditor)
|
|
||||||
{
|
|
||||||
// make a random Pose
|
|
||||||
//var pos = new Vector3(UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f));
|
|
||||||
//var rot = new Quaternion(UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f));
|
|
||||||
var pos = Vector3.up;
|
|
||||||
var rot = Quaternion.identity;
|
|
||||||
rot.Normalize();
|
|
||||||
pose = new Pose(pos, rot);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// If the xrBaseSpace is changed, the pose will be updated.
|
// If the xrBaseSpace is changed, the pose will be updated.
|
||||||
pose = default;
|
pose = default;
|
||||||
XrSpaceLocation location = new XrSpaceLocation();
|
XrSpaceLocation location = new XrSpaceLocation();
|
||||||
@@ -179,14 +180,14 @@ namespace VIVE.OpenXR.Feature
|
|||||||
|
|
||||||
if (ret != XrResult.XR_SUCCESS)
|
if (ret != XrResult.XR_SUCCESS)
|
||||||
{
|
{
|
||||||
Debug.Log("Space: LocateSpace ret=" + ret);
|
//Debug.Log("Space: LocateSpace ret=" + ret);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Log("Space: baseSpace=" + baseSpace + ", space=" + space + ", time=" + time + ", ret=" + ret);
|
//Debug.Log("Space: baseSpace=" + baseSpace + ", space=" + space + ", time=" + time + ", ret=" + ret);
|
||||||
Debug.Log("Space: location.locationFlags=" + location.locationFlags);
|
//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.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);
|
//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 &&
|
if ((location.locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_POSITION_VALID_BIT) > 0 &&
|
||||||
(location.locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) > 0)
|
(location.locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) > 0)
|
||||||
{
|
{
|
||||||
@@ -211,7 +212,7 @@ namespace VIVE.OpenXR.Feature
|
|||||||
// Managered resource
|
// Managered resource
|
||||||
}
|
}
|
||||||
// Non managered resource
|
// Non managered resource
|
||||||
Debug.Log($"Space: DestroySpace({space})");
|
//Debug.Log($"Space: DestroySpace({space})");
|
||||||
SpaceWrapper.Instance.DestroySpace(space);
|
SpaceWrapper.Instance.DestroySpace(space);
|
||||||
space = 0;
|
space = 0;
|
||||||
disposed = true;
|
disposed = true;
|
||||||
|
|||||||
@@ -3,9 +3,27 @@ using System.Runtime.InteropServices;
|
|||||||
using System;
|
using System;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using AOT;
|
using AOT;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace VIVE.OpenXR
|
namespace VIVE.OpenXR
|
||||||
{
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||||
|
internal class HookHandlerAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string xrFuncName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set this function to handle the hook process in <see cref="ViveInterceptors.XrGetInstanceProcAddrInterceptor" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xrFuncName">The hooked openxr function name</param>
|
||||||
|
public HookHandlerAttribute(string xrFuncName)
|
||||||
|
{
|
||||||
|
this.xrFuncName = xrFuncName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class is made for all features that need to intercept OpenXR API calls.
|
/// 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.
|
/// Some APIs will be called by Unity internally, and we need to intercept them in c# to get some information.
|
||||||
@@ -17,12 +35,28 @@ namespace VIVE.OpenXR
|
|||||||
/// For example:
|
/// For example:
|
||||||
/// protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
/// protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
/// {
|
/// {
|
||||||
/// return HtcInterceptors.Instance.HookGetInstanceProcAddr(func);
|
/// return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
|
||||||
/// }
|
/// }
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
|
// For extending the ViveInterceptors class, create a new partial class and implement the required functions.
|
||||||
|
// For example:
|
||||||
|
// public partial class ViveInterceptors
|
||||||
|
// {
|
||||||
|
// [HookHandler("xrYourFunction")]
|
||||||
|
// private static XrResult OnHookXrYourFunction(XrInstance instance, string name, out IntPtr function)
|
||||||
|
// { ... }
|
||||||
|
// }
|
||||||
partial class ViveInterceptors
|
partial class ViveInterceptors
|
||||||
{
|
{
|
||||||
public const string TAG = "Interceptors";
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static ViveInterceptors instance = null;
|
public static ViveInterceptors instance = null;
|
||||||
public static ViveInterceptors Instance
|
public static ViveInterceptors Instance
|
||||||
@@ -37,47 +71,129 @@ namespace VIVE.OpenXR
|
|||||||
|
|
||||||
public ViveInterceptors()
|
public ViveInterceptors()
|
||||||
{
|
{
|
||||||
Debug.Log("HtcInterceptors");
|
Log.D("ViveInterceptors");
|
||||||
|
RegisterFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInited = false;
|
delegate XrResult HookHandler(XrInstance instance, string name, out IntPtr function);
|
||||||
|
static readonly Dictionary<string, HookHandler> interceptors = new Dictionary<string, HookHandler>();
|
||||||
|
|
||||||
public delegate XrResult DelegateXrGetInstanceProcAddr(XrInstance instance, string name, out IntPtr function);
|
private static void RegisterFunctions()
|
||||||
private static readonly DelegateXrGetInstanceProcAddr hookXrGetInstanceProcAddrHandle = new DelegateXrGetInstanceProcAddr(XrGetInstanceProcAddrInterceptor);
|
{
|
||||||
|
var methods = typeof(ViveInterceptors).GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
|
||||||
|
foreach (var method in methods)
|
||||||
|
{
|
||||||
|
var attribute = method.GetCustomAttributes(typeof(HookHandlerAttribute), false).FirstOrDefault() as HookHandlerAttribute;
|
||||||
|
if (attribute != null)
|
||||||
|
{
|
||||||
|
Log.I(TAG, $"Registering hook handler {attribute.xrFuncName}");
|
||||||
|
interceptors.Add(attribute.xrFuncName, (HookHandler)method.CreateDelegate(typeof(HookHandler)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly OpenXRHelper.xrGetInstanceProcAddrDelegate hookXrGetInstanceProcAddrHandle = new OpenXRHelper.xrGetInstanceProcAddrDelegate(XrGetInstanceProcAddrInterceptor);
|
||||||
private static readonly IntPtr hookGetInstanceProcAddrHandlePtr = Marshal.GetFunctionPointerForDelegate(hookXrGetInstanceProcAddrHandle);
|
private static readonly IntPtr hookGetInstanceProcAddrHandlePtr = Marshal.GetFunctionPointerForDelegate(hookXrGetInstanceProcAddrHandle);
|
||||||
static DelegateXrGetInstanceProcAddr XrGetInstanceProcAddrOriginal = null;
|
static OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddrOriginal = null;
|
||||||
|
|
||||||
[MonoPInvokeCallback(typeof(DelegateXrGetInstanceProcAddr))]
|
[MonoPInvokeCallback(typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate))]
|
||||||
private static XrResult XrGetInstanceProcAddrInterceptor(XrInstance instance, string name, out IntPtr function)
|
private static XrResult XrGetInstanceProcAddrInterceptor(XrInstance instance, string name, out IntPtr function)
|
||||||
{
|
{
|
||||||
// Custom interceptors
|
// Used to check if the original function is already hooked.
|
||||||
if (name == "xrWaitFrame")
|
if (instance == 0 && name == "ViveInterceptorHooked")
|
||||||
{
|
{
|
||||||
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
|
function = IntPtr.Zero;
|
||||||
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
return XrResult.XR_SUCCESS;
|
||||||
if (ret == XrResult.XR_SUCCESS)
|
|
||||||
{
|
|
||||||
XrWaitFrameOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrWaitFrame>(function);
|
|
||||||
function = xrWaitFrameInterceptorPtr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the function is intercepted by other features
|
||||||
|
if (interceptors.ContainsKey(name))
|
||||||
|
{
|
||||||
|
// If no request for this function, call the original function directly.
|
||||||
|
if (!requiredFunctions.Contains(name))
|
||||||
|
return XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||||
|
|
||||||
|
var ret = interceptors[name](instance, name, out function);
|
||||||
|
if (ret == XrResult.XR_SUCCESS)
|
||||||
|
Log.I(TAG, name + " is intercepted");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return XrGetInstanceProcAddrOriginal(instance, name, out function);
|
return XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IntPtr HookGetInstanceProcAddr(IntPtr func)
|
public IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
{
|
{
|
||||||
Debug.Log($"{TAG}: registering our own xrGetInstanceProcAddr");
|
Log.D(TAG, "HookGetInstanceProcAddr");
|
||||||
if (XrGetInstanceProcAddrOriginal == null)
|
if (XrGetInstanceProcAddrOriginal == null)
|
||||||
{
|
{
|
||||||
XrGetInstanceProcAddrOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrGetInstanceProcAddr>(func);
|
Log.D(TAG, "registering our own xrGetInstanceProcAddr");
|
||||||
isInited = true;
|
XrGetInstanceProcAddrOriginal = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrGetInstanceProcAddrDelegate>(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.
|
||||||
|
Log.D(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.
|
||||||
|
Log.D(TAG, "Already hooked");
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return hookGetInstanceProcAddrHandlePtr;
|
return hookGetInstanceProcAddrHandlePtr;
|
||||||
}
|
}
|
||||||
else
|
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;
|
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)
|
||||||
|
{
|
||||||
|
Log.D(TAG, $"AddRequiredFunction({name})");
|
||||||
|
if (!interceptors.ContainsKey(name))
|
||||||
|
{
|
||||||
|
Log.E(TAG, $"AddRequiredFunction({name}) failed. No such function.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!requiredFunctions.Contains(name))
|
||||||
|
requiredFunctions.Add(name);
|
||||||
|
|
||||||
|
// If your function support unregister, you can add the reference count here.
|
||||||
|
if (name == "xrLocateViews")
|
||||||
|
xrLocateViewsReferenceCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If no need to use this hooked function, call this will remove your requirement.
|
||||||
|
/// If all requirements are removed, the original function will be called directly.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
public void RemoveRequiredFunction(string name)
|
||||||
|
{
|
||||||
|
// If your function support unregister, you can add the reference count here.
|
||||||
|
if (requiredFunctions.Contains(name))
|
||||||
|
{
|
||||||
|
if (name == "xrLocateViews")
|
||||||
|
xrLocateViewsReferenceCount = Mathf.Max(xrLocateViewsReferenceCount--, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
// 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
|
||||||
|
{
|
||||||
|
[HookHandler("xrBeginSession")]
|
||||||
|
private static XrResult OnHookXrBeginSession(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
if (xrBeginSessionOrigin == null)
|
||||||
|
{
|
||||||
|
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||||
|
if (ret != XrResult.XR_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
xrBeginSessionOrigin = Marshal.GetDelegateForFunctionPointer<xrBeginSessionDelegate>(function);
|
||||||
|
}
|
||||||
|
function = xrBeginSessionPtr;
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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("VI: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(")");
|
||||||
|
Log.D(sb);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
result = xrBeginSessionOrigin(session, ref beginInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.E("xrBeginSessionInterceptor() Not assign xrBeginSession!");
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
Log.D(sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8c222b96d7eb4ca4bb6390e07b1967bb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System;
|
||||||
|
using AOT;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
public partial class ViveInterceptors
|
||||||
|
{
|
||||||
|
[HookHandler("xrLocateViews")]
|
||||||
|
private static XrResult OnHookXrLocateViews(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
if (xrLocateViewsOriginal == null)
|
||||||
|
{
|
||||||
|
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||||
|
if (ret != XrResult.XR_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
xrLocateViewsOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrLocateViews>(function);
|
||||||
|
}
|
||||||
|
function = xrLocateViewsInterceptorPtr;
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct XrViewLocateInfo
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public XrViewConfigurationType viewConfigurationType;
|
||||||
|
public XrTime displayTime;
|
||||||
|
public XrSpace space;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct XrView
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public XrPosef pose;
|
||||||
|
public XrFovf fov;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum XrViewStateFlags {
|
||||||
|
ORIENTATION_VALID_BIT = 0x00000001,
|
||||||
|
POSITION_VALID_BIT = 0x00000002,
|
||||||
|
ORIENTATION_TRACKED_BIT = 0x00000004,
|
||||||
|
POSITION_TRACKED_BIT = 0x00000008,
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct XrViewState
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public XrViewStateFlags viewStateFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate XrResult DelegateXrLocateViews(XrSession session, IntPtr /*XrViewLocateInfo*/ viewLocateInfo, IntPtr /*XrViewState*/ viewState, uint viewCapacityInput, ref uint viewCountOutput, IntPtr /*XrView*/ views);
|
||||||
|
|
||||||
|
private static readonly DelegateXrLocateViews xrLocateViewsInterceptorHandle = new DelegateXrLocateViews(XrLocateViewsInterceptor);
|
||||||
|
private static readonly IntPtr xrLocateViewsInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrLocateViewsInterceptorHandle);
|
||||||
|
static DelegateXrLocateViews xrLocateViewsOriginal = null;
|
||||||
|
static int xrLocateViewsReferenceCount = 0;
|
||||||
|
|
||||||
|
[MonoPInvokeCallback(typeof(DelegateXrLocateViews))]
|
||||||
|
private static XrResult XrLocateViewsInterceptor(XrSession session, IntPtr viewLocateInfo, IntPtr viewState, uint viewCapacityInput, ref uint viewCountOutput, IntPtr views)
|
||||||
|
{
|
||||||
|
// Call the original function if the reference count is less than or equal to 0
|
||||||
|
if (xrLocateViewsReferenceCount <= 0)
|
||||||
|
return xrLocateViewsOriginal(session, viewLocateInfo, viewState, viewCapacityInput, ref viewCountOutput, views);
|
||||||
|
|
||||||
|
Profiler.BeginSample("VI:LocateViewsA");
|
||||||
|
XrResult result = XrResult.XR_SUCCESS;
|
||||||
|
if (instance.BeforeOriginalLocateViews != null)
|
||||||
|
instance.BeforeOriginalLocateViews(session, viewLocateInfo, viewState, viewCapacityInput, ref viewCountOutput, views);
|
||||||
|
Profiler.EndSample();
|
||||||
|
result = xrLocateViewsOriginal(session, viewLocateInfo, viewState, viewCapacityInput, ref viewCountOutput, views);
|
||||||
|
Profiler.BeginSample("VI:LocateViewsB");
|
||||||
|
instance.AfterOriginalLocateViews?.Invoke(session, viewLocateInfo, viewState, viewCapacityInput, ref viewCountOutput, views);
|
||||||
|
Profiler.EndSample();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If you return false, the original function will not be called.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public delegate bool DelegateXrLocateViewsInterceptor(XrSession session, IntPtr viewLocateInfo, IntPtr viewState, uint viewCapacityInput, ref uint viewCountOutput, IntPtr views);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this to intercept the original function. This will be called before the original function.
|
||||||
|
/// </summary>
|
||||||
|
public DelegateXrLocateViewsInterceptor BeforeOriginalLocateViews;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this to intercept the original function. This will be called after the original function.
|
||||||
|
/// </summary>
|
||||||
|
public DelegateXrLocateViewsInterceptor AfterOriginalLocateViews;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5dfd24c69475c3740975bf5538de3869
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
// 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
|
||||||
|
{
|
||||||
|
[HookHandler("xrPollEvent")]
|
||||||
|
private static XrResult OnHookXrPollEvent(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
if (xrPollEventOrigin == null)
|
||||||
|
{
|
||||||
|
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||||
|
if (ret != XrResult.XR_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
xrPollEventOrigin = Marshal.GetDelegateForFunctionPointer<xrPollEventDelegate>(function);
|
||||||
|
}
|
||||||
|
function = xrPollEventPtr;
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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("VI:PollEvent");
|
||||||
|
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); Log.D("PollEvent", 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);
|
||||||
|
Log.D("PollEvent", sb.ToString());
|
||||||
|
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);
|
||||||
|
Log.D("PollEvent", 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);
|
||||||
|
Log.D("PollEvent", 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);
|
||||||
|
Log.D("PollEvent", 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);
|
||||||
|
Log.D("PollEvent", sb);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//sb.Clear().Append("xrPollEventInterceptor() xrPollEvent result: ").Append(result).Append(", isUserPresent: ").Append(isUserPresent); Log.d("PollEvent", 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c2cc5716d3f563f49a47da6c1bd8ccbe
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
104
com.htc.upm.vive.openxr/Runtime/Common/ViveInterceptorsSubmit.cs
Normal file
104
com.htc.upm.vive.openxr/Runtime/Common/ViveInterceptorsSubmit.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System;
|
||||||
|
using AOT;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
partial class ViveInterceptors
|
||||||
|
{
|
||||||
|
[HookHandler("xrEndFrame")]
|
||||||
|
private static XrResult OnHookXrEndFrame(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
if (XrEndFrameOriginal == null)
|
||||||
|
{
|
||||||
|
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||||
|
if (ret != XrResult.XR_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
XrEndFrameOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrEndFrame>(function);
|
||||||
|
}
|
||||||
|
function = xrEndFrameInterceptorPtr;
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
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:EndFrameB");
|
||||||
|
XrResult result = XrResult.XR_SUCCESS;
|
||||||
|
bool ret = true;
|
||||||
|
if (instance.BeforeOriginalEndFrame != null)
|
||||||
|
ret = instance.BeforeOriginalEndFrame(session, ref frameEndInfo, ref result);
|
||||||
|
Profiler.EndSample();
|
||||||
|
if (!ret)
|
||||||
|
return result;
|
||||||
|
result = XrEndFrameOriginal(session, ref frameEndInfo);
|
||||||
|
Profiler.BeginSample("VI:EndFrameA");
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6bf7cf55d82ac6343b4eda92d1197a66
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System;
|
||||||
|
using AOT;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
public partial class ViveInterceptors
|
||||||
|
{
|
||||||
|
[HookHandler("xrGetVisibilityMaskKHR")]
|
||||||
|
private static XrResult OnHookXrGetVisibilityMaskKHR(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
if (xrGetVisibilityMaskKHROriginal == null)
|
||||||
|
{
|
||||||
|
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||||
|
if (ret != XrResult.XR_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
xrGetVisibilityMaskKHROriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrGetVisibilityMaskKHR>(function);
|
||||||
|
}
|
||||||
|
function = xrGetVisibilityMaskKHRInterceptorPtr;
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum XrVisibilityMaskTypeKHR
|
||||||
|
{
|
||||||
|
HIDDEN_TRIANGLE_MESH_KHR = 1,
|
||||||
|
VISIBLE_TRIANGLE_MESH_KHR = 2,
|
||||||
|
LINE_LOOP_KHR = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct XrVisibilityMaskKHR
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public uint vertexCapacityInput;
|
||||||
|
public uint vertexCountOutput;
|
||||||
|
public IntPtr vertices; // XrVector2f array
|
||||||
|
public uint indexCapacityInput;
|
||||||
|
public uint indexCountOutput;
|
||||||
|
public IntPtr indices; // uint array
|
||||||
|
}
|
||||||
|
|
||||||
|
// XrCompositionLayerSpaceWarpInfoFlagsFB bits
|
||||||
|
public delegate XrResult DelegateXrGetVisibilityMaskKHR(XrSession session, XrViewConfigurationType viewConfigurationType, uint viewIndex, XrVisibilityMaskTypeKHR visibilityMaskType, ref XrVisibilityMaskKHR visibilityMask);
|
||||||
|
|
||||||
|
private static readonly DelegateXrGetVisibilityMaskKHR xrGetVisibilityMaskKHRInterceptorHandle = new DelegateXrGetVisibilityMaskKHR(XrGetVisibilityMaskKHRInterceptor);
|
||||||
|
private static readonly IntPtr xrGetVisibilityMaskKHRInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrGetVisibilityMaskKHRInterceptorHandle);
|
||||||
|
static DelegateXrGetVisibilityMaskKHR xrGetVisibilityMaskKHROriginal = null;
|
||||||
|
|
||||||
|
[MonoPInvokeCallback(typeof(DelegateXrGetVisibilityMaskKHR))]
|
||||||
|
private static XrResult XrGetVisibilityMaskKHRInterceptor(XrSession session, XrViewConfigurationType viewConfigurationType, uint viewIndex, XrVisibilityMaskTypeKHR visibilityMaskType, ref XrVisibilityMaskKHR visibilityMask)
|
||||||
|
{
|
||||||
|
// instance must not null
|
||||||
|
//if (instance == null)
|
||||||
|
// return XrGetVisibilityMaskKHROriginal(session, ref frameEndInfo);
|
||||||
|
Profiler.BeginSample("VI:GetVMB");
|
||||||
|
XrResult result = XrResult.XR_SUCCESS;
|
||||||
|
bool ret = true;
|
||||||
|
if (instance.BeforeOriginalGetVisibilityMaskKHR != null)
|
||||||
|
ret = instance.BeforeOriginalGetVisibilityMaskKHR(session, viewConfigurationType, viewIndex, visibilityMaskType, ref visibilityMask, ref result);
|
||||||
|
Profiler.EndSample();
|
||||||
|
if (!ret)
|
||||||
|
return result;
|
||||||
|
result = xrGetVisibilityMaskKHROriginal(session, viewConfigurationType, viewIndex, visibilityMaskType, ref visibilityMask);
|
||||||
|
Profiler.BeginSample("VI:GetVMA");
|
||||||
|
instance.AfterOriginalGetVisibilityMaskKHR?.Invoke(session, viewConfigurationType, viewIndex, visibilityMaskType, ref visibilityMask, 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 DelegateXrGetVisibilityMaskKHRInterceptor(XrSession session, XrViewConfigurationType viewConfigurationType, uint viewIndex, XrVisibilityMaskTypeKHR visibilityMaskType, ref XrVisibilityMaskKHR visibilityMask, ref XrResult result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this to intercept the original function. This will be called before the original function.
|
||||||
|
/// </summary>
|
||||||
|
public DelegateXrGetVisibilityMaskKHRInterceptor BeforeOriginalGetVisibilityMaskKHR;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this to intercept the original function. This will be called after the original function.
|
||||||
|
/// </summary>
|
||||||
|
public DelegateXrGetVisibilityMaskKHRInterceptor AfterOriginalGetVisibilityMaskKHR;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a4c00b0b7df78d34d89cd728c9de0672
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -3,12 +3,26 @@ using System.Runtime.InteropServices;
|
|||||||
using System;
|
using System;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using AOT;
|
using AOT;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
|
||||||
namespace VIVE.OpenXR
|
namespace VIVE.OpenXR
|
||||||
{
|
{
|
||||||
partial class ViveInterceptors
|
partial class ViveInterceptors
|
||||||
{
|
{
|
||||||
#region XRWaitFrame
|
[HookHandler("xrWaitFrame")]
|
||||||
|
private static XrResult OnHookXrWaitFrame(XrInstance instance, string name, out IntPtr function)
|
||||||
|
{
|
||||||
|
if (XrWaitFrameOriginal == null)
|
||||||
|
{
|
||||||
|
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||||
|
if (ret != XrResult.XR_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
XrWaitFrameOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrWaitFrame>(function);
|
||||||
|
}
|
||||||
|
function = xrWaitFrameInterceptorPtr;
|
||||||
|
return XrResult.XR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
public struct XrFrameWaitInfo
|
public struct XrFrameWaitInfo
|
||||||
{
|
{
|
||||||
public XrStructureType type;
|
public XrStructureType type;
|
||||||
@@ -24,6 +38,8 @@ namespace VIVE.OpenXR
|
|||||||
public XrBool32 shouldRender;
|
public XrBool32 shouldRender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isWaitFrameIntercepted = false;
|
||||||
|
|
||||||
public delegate XrResult DelegateXrWaitFrame(XrSession session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState);
|
public delegate XrResult DelegateXrWaitFrame(XrSession session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState);
|
||||||
private static readonly DelegateXrWaitFrame xrWaitFrameInterceptorHandle = new DelegateXrWaitFrame(XrWaitFrameInterceptor);
|
private static readonly DelegateXrWaitFrame xrWaitFrameInterceptorHandle = new DelegateXrWaitFrame(XrWaitFrameInterceptor);
|
||||||
private static readonly IntPtr xrWaitFrameInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrWaitFrameInterceptorHandle);
|
private static readonly IntPtr xrWaitFrameInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrWaitFrameInterceptorHandle);
|
||||||
@@ -32,30 +48,73 @@ namespace VIVE.OpenXR
|
|||||||
[MonoPInvokeCallback(typeof(DelegateXrWaitFrame))]
|
[MonoPInvokeCallback(typeof(DelegateXrWaitFrame))]
|
||||||
private static XrResult XrWaitFrameInterceptor(XrSession session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState)
|
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);
|
var ret = XrWaitFrameOriginal(session, ref frameWaitInfo, ref frameState);
|
||||||
|
instance.AfterOriginalWaitFrame?.Invoke(session, ref frameWaitInfo, ref frameState, ref result);
|
||||||
currentFrameState = frameState;
|
currentFrameState = frameState;
|
||||||
return ret;
|
Profiler.EndSample();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static XrFrameState currentFrameState = new XrFrameState() { predictedDisplayTime = 0 };
|
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()
|
public XrFrameState GetCurrentFrameState()
|
||||||
{
|
{
|
||||||
if (!isInited) throw new Exception("ViveInterceptors is not inited");
|
if (!isWaitFrameIntercepted) throw new Exception("ViveInterceptors is not intercepted");
|
||||||
|
|
||||||
return currentFrameState;
|
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()
|
public XrTime GetPredictTime()
|
||||||
{
|
{
|
||||||
if (!isInited) throw new Exception("ViveInterceptors is not inited");
|
if (!isWaitFrameIntercepted) throw new Exception("ViveInterceptors is not intercepted");
|
||||||
|
|
||||||
Debug.Log($"{TAG}: XrWaitFrameInterceptor(predictedDisplayTime={currentFrameState.predictedDisplayTime}");
|
//Debug.Log($"{TAG}: XrWaitFrameInterceptor(predictedDisplayTime={currentFrameState.predictedDisplayTime}");
|
||||||
if (currentFrameState.predictedDisplayTime == 0)
|
if (currentFrameState.predictedDisplayTime == 0)
|
||||||
return new XrTime((long)(1000000L * (Time.unscaledTimeAsDouble + 0.011f)));
|
return new XrTime((long)(1000000L * (Time.unscaledTimeAsDouble + 0.011f)));
|
||||||
else
|
else
|
||||||
return currentFrameState.predictedDisplayTime;
|
return currentFrameState.predictedDisplayTime;
|
||||||
}
|
}
|
||||||
#endregion XRWaitFrame
|
|
||||||
|
/// <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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
247
com.htc.upm.vive.openxr/Runtime/Common/ViveLog.cs
Normal file
247
com.htc.upm.vive.openxr/Runtime/Common/ViveLog.cs
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
#endif
|
||||||
|
using System.Text;
|
||||||
|
// Non android will need UnityEngine
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR
|
||||||
|
{
|
||||||
|
public static class Log
|
||||||
|
{
|
||||||
|
public const string TAG = "VIVE.OpenXR";
|
||||||
|
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
[DllImport("liblog.so")]
|
||||||
|
private static extern int __android_log_print(int prio, string tag, string fmt, string msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Use ("%s", message) instead of just (message) is because of the following reason:
|
||||||
|
// In case message contains special characters like %, \n, \r, etc. It will be treated as format string.
|
||||||
|
// This is a little waste of performance, but it's safer.
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Not show in Standalone
|
||||||
|
/// </summary>
|
||||||
|
public static void D(string message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(3, TAG, "%s", message); // Android Debug
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void I(string message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(4, TAG, "%s", message); // Android Info
|
||||||
|
#else
|
||||||
|
Debug.Log(message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void W(string message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(5, TAG, "%s", message); // Android Warning
|
||||||
|
#else
|
||||||
|
Debug.LogWarning(message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void E(string message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(6, TAG, "%s", message); // Android Error
|
||||||
|
#else
|
||||||
|
Debug.LogError(message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Not show in Standalone
|
||||||
|
/// </summary>
|
||||||
|
public static void D(string tag, string message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(3, tag, "%s", message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void I(string tag, string message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(4, tag, "%s", message);
|
||||||
|
#else
|
||||||
|
Debug.LogFormat("{0}: {1}", tag, message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void W(string tag, string message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(5, tag, "%s", message); // Android Warning
|
||||||
|
#else
|
||||||
|
Debug.LogWarningFormat("{0}: {1}", tag, message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void E(string tag, string message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(6, tag, "%s", message); // Android Error
|
||||||
|
#else
|
||||||
|
Debug.LogErrorFormat("{0}: {1}", tag, message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Not show in Standalone
|
||||||
|
/// </summary>
|
||||||
|
public static void D(StringBuilder message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(3, TAG, "%s", message.ToString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void I(StringBuilder message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(4, TAG, "%s", message.ToString());
|
||||||
|
#else
|
||||||
|
Debug.Log(message.ToString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void W(StringBuilder message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(5, TAG, "%s", message.ToString()); // Android Warning
|
||||||
|
#else
|
||||||
|
Debug.LogWarning(message.ToString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void E(StringBuilder message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(6, TAG, "%s", message.ToString()); // Android Error
|
||||||
|
#else
|
||||||
|
Debug.LogError(message.ToString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Not show in Standalone
|
||||||
|
/// </summary>
|
||||||
|
public static void D(string tag, StringBuilder message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(3, tag, "%s", message.ToString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void I(string tag, StringBuilder message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(4, tag, "%s", message.ToString());
|
||||||
|
#else
|
||||||
|
Debug.LogFormat("{0}: {1}", tag, message.ToString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void W(string tag, StringBuilder message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(5, tag, "%s", message.ToString()); // Android Warning
|
||||||
|
#else
|
||||||
|
Debug.LogWarningFormat("{0}: {1}", tag, message.ToString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void E(string tag, StringBuilder message)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(6, tag, "%s", message.ToString()); // Android Error
|
||||||
|
#else
|
||||||
|
Debug.LogErrorFormat("{0}: {1}", tag, message.ToString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Not show in Standalone
|
||||||
|
/// </summary>
|
||||||
|
public static void DFmt(string fmt, params object[] args)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(3, TAG, "%s", string.Format(fmt, args));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void IFmt(string fmt, params object[] args)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(4, TAG, "%s", string.Format(fmt, args));
|
||||||
|
#else
|
||||||
|
Debug.LogFormat(fmt, args);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WFmt(string fmt, params object[] args)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(5, TAG, "%s", string.Format(fmt, args)); // Android Warning
|
||||||
|
#else
|
||||||
|
Debug.LogWarningFormat(fmt, args);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EFmt(string fmt, params object[] args)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(6, TAG, "%s", string.Format(fmt, args)); // Android Error
|
||||||
|
#else
|
||||||
|
Debug.LogErrorFormat(fmt, args);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Not show in Standalone
|
||||||
|
/// </summary>
|
||||||
|
public static void DFmt(string tag, string fmt, params object[] args)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(3, tag, "%s", string.Format(fmt, args));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void IFmt(string tag, string fmt, params object[] args)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(4, tag, "%s", string.Format(fmt, args));
|
||||||
|
#else
|
||||||
|
Debug.LogFormat("{0}: {1}", tag, string.Format(fmt, args));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WFmt(string tag, string fmt, params object[] args)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(5, tag, "%s", string.Format(fmt, args)); // Android Warning
|
||||||
|
#else
|
||||||
|
Debug.LogWarningFormat("{0}: {1}", tag, fmt, string.Format(fmt, args));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EFmt(string tag, string fmt, params object[] args)
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
__android_log_print(6, tag, "%s", string.Format(fmt, args)); // Android Error
|
||||||
|
#else
|
||||||
|
Debug.LogErrorFormat("{0}: {1}", tag, fmt, string.Format(fmt, args));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
com.htc.upm.vive.openxr/Runtime/Common/ViveLog.cs.meta
Normal file
11
com.htc.upm.vive.openxr/Runtime/Common/ViveLog.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9750d8d4e8eb4994088534cb111510d3
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
338
com.htc.upm.vive.openxr/Runtime/Common/ViveRenderThreadTask.cs
Normal file
338
com.htc.upm.vive.openxr/Runtime/Common/ViveRenderThreadTask.cs
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
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);
|
||||||
|
//Log.d("RT.MessagePool.Obtain<" + typeof(T) + ">() pool count=" + pool.Count); // Not to expose developer's type.
|
||||||
|
Log.D("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()
|
||||||
|
{
|
||||||
|
// Remove could be in a random order, and will cause orderId change. DO not remove any of them.
|
||||||
|
//try { CommandList.Remove(this); } 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;
|
||||||
|
if (msg != null)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
if (msg != null)
|
||||||
|
{
|
||||||
|
// 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("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
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 251b4bedf6420fc4e84be778e501343f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -11,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";
|
||||||
@@ -79,7 +80,7 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
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.
|
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
|
XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PLANAR_HTC
|
||||||
);
|
);
|
||||||
XrResult res = XR_HTC_passthrough.xrCreatePassthroughHTC(createInfo, out passthrough);
|
XrResult res = passthroughFeature.CreatePassthroughHTC(createInfo, out passthrough);
|
||||||
if(res == XrResult.XR_SUCCESS)
|
if(res == XrResult.XR_SUCCESS)
|
||||||
{
|
{
|
||||||
ulong passthrough_ulong = passthrough;
|
ulong passthrough_ulong = passthrough;
|
||||||
@@ -192,7 +193,7 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
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.
|
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
|
XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PROJECTED_HTC
|
||||||
);
|
);
|
||||||
XrResult res = XR_HTC_passthrough.xrCreatePassthroughHTC(createInfo, out passthrough);
|
XrResult res = passthroughFeature.CreatePassthroughHTC(createInfo, out passthrough);
|
||||||
if (res == XrResult.XR_SUCCESS)
|
if (res == XrResult.XR_SUCCESS)
|
||||||
{
|
{
|
||||||
ulong passthrough_ulong = passthrough;
|
ulong passthrough_ulong = passthrough;
|
||||||
@@ -301,7 +302,7 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
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.
|
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
|
XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PROJECTED_HTC
|
||||||
);
|
);
|
||||||
XrResult res = XR_HTC_passthrough.xrCreatePassthroughHTC(createInfo, out passthrough);
|
XrResult res = passthroughFeature.CreatePassthroughHTC(createInfo, out passthrough);
|
||||||
if (res == XrResult.XR_SUCCESS)
|
if (res == XrResult.XR_SUCCESS)
|
||||||
{
|
{
|
||||||
ulong passthrough_ulong = passthrough;
|
ulong passthrough_ulong = passthrough;
|
||||||
@@ -359,10 +360,10 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
#if UNITY_STANDALONE
|
#if UNITY_STANDALONE
|
||||||
private static void SubmitLayer()
|
private static void SubmitLayer()
|
||||||
{
|
{
|
||||||
|
XR_HTC_passthrough.Interop.GetOriginEndFrameLayerList(out List<IntPtr> layerList);//GetOriginEndFrameLayers
|
||||||
foreach(var passthrough in passthrough2IsUnderLay.Keys)
|
foreach(var passthrough in passthrough2IsUnderLay.Keys)
|
||||||
{
|
{
|
||||||
//Get and submit layer list
|
//Get and submit layer list
|
||||||
XR_HTC_passthrough.Interop.GetOriginEndFrameLayerList(out List<IntPtr> layerList);//GetOriginEndFrameLayers
|
|
||||||
if (layerList.Count != 0)
|
if (layerList.Count != 0)
|
||||||
{
|
{
|
||||||
Marshal.StructureToPtr(passthrough2Layer[passthrough], passthrough2LayerPtr[passthrough], false);
|
Marshal.StructureToPtr(passthrough2Layer[passthrough], passthrough2LayerPtr[passthrough], false);
|
||||||
@@ -370,10 +371,10 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
layerList.Insert(0, passthrough2LayerPtr[passthrough]);
|
layerList.Insert(0, passthrough2LayerPtr[passthrough]);
|
||||||
else
|
else
|
||||||
layerList.Insert(1, passthrough2LayerPtr[passthrough]);
|
layerList.Insert(1, passthrough2LayerPtr[passthrough]);
|
||||||
|
}
|
||||||
|
}
|
||||||
XR_HTC_passthrough.Interop.SubmitLayers(layerList);
|
XR_HTC_passthrough.Interop.SubmitLayers(layerList);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -393,21 +394,23 @@ 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
|
#if UNITY_STANDALONE
|
||||||
XrPassthroughHTC passthrough = (XrPassthroughHTC)(ulong)passthroughID;
|
XrPassthroughHTC passthrough = passthrough2Layer[passthroughID].passthrough;
|
||||||
passthrough2Layer.Remove(passthroughID);
|
passthroughFeature.DestroyPassthroughHTC(passthrough);
|
||||||
Marshal.FreeHGlobal(passthrough2LayerPtr[passthroughID]);
|
|
||||||
passthrough2LayerPtr.Remove(passthroughID);
|
|
||||||
passthrough2IsUnderLay.Remove(passthroughID);
|
passthrough2IsUnderLay.Remove(passthroughID);
|
||||||
Marshal.FreeHGlobal(passthrough2meshTransformInfoPtr[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);
|
passthrough2meshTransformInfoPtr.Remove(passthroughID);
|
||||||
passthrough2meshTransform.Remove(passthroughID);
|
passthrough2meshTransform.Remove(passthroughID);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
#if UNITY_ANDROID
|
#if UNITY_ANDROID
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 4e5bee8db40a5a941a38710195e3219e
|
guid: a6509bdf37b3b364eb80cb0df68435a3
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: ca57a546da07d9146aa710d82ec06e64
|
guid: 5e0cbfbe15682c542acc5675d4503f72
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a3dfbc6bb6d75454db700d2326157424
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 050772d662d04514ca3bb28fbe82ecd7
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d25b2e9fff2d6724b865e0fbd609da9d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
com.htc.upm.vive.openxr/Runtime/Editor/Interactions.meta
Normal file
8
com.htc.upm.vive.openxr/Runtime/Editor/Interactions.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a8bd17374612cce468393aa1acc9fa89
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
// 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
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c7e32703a3206194580e534565abcf91
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
44
com.htc.upm.vive.openxr/Runtime/Editor/VIVERigEditor.cs
Normal file
44
com.htc.upm.vive.openxr/Runtime/Editor/VIVERigEditor.cs
Normal 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
|
||||||
11
com.htc.upm.vive.openxr/Runtime/Editor/VIVERigEditor.cs.meta
Normal file
11
com.htc.upm.vive.openxr/Runtime/Editor/VIVERigEditor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4766014dc7f94c8468710cc3fd265f90
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright HTC Corporation All Rights Reserved.
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
// Remove FAKE_DATA if editor or windows is supported.
|
// Remove FAKE_DATA if editor or windows is supported.
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
@@ -10,39 +10,56 @@ using System.Runtime.InteropServices;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.XR.OpenXR;
|
using UnityEngine.XR.OpenXR;
|
||||||
using UnityEngine.XR.OpenXR.Features;
|
using UnityEngine.XR.OpenXR.Features;
|
||||||
using VIVE.OpenXR.Feature;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.XR.OpenXR.Features;
|
using UnityEditor.XR.OpenXR.Features;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace VIVE.OpenXR.Anchor
|
namespace VIVE.OpenXR.Feature
|
||||||
{
|
{
|
||||||
|
using XrPersistedAnchorCollectionHTC = System.IntPtr;
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
[OpenXRFeature(UiName = "VIVE XR Anchor",
|
[OpenXRFeature(UiName = "VIVE XR Anchor (Beta)",
|
||||||
Desc = "VIVE's implementaion of the XR_HTC_anchor.",
|
Desc = "VIVE's implementaion of the XR_HTC_anchor.",
|
||||||
Company = "HTC",
|
Company = "HTC",
|
||||||
DocumentationLink = "..\\Documentation",
|
DocumentationLink = "..\\Documentation",
|
||||||
OpenxrExtensionStrings = kOpenxrExtensionString,
|
OpenxrExtensionStrings = kOpenxrExtensionString,
|
||||||
Version = "1.0.0",
|
Version = "1.0.0",
|
||||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone },
|
||||||
FeatureId = featureId
|
FeatureId = featureId
|
||||||
)]
|
)]
|
||||||
#endif
|
#endif
|
||||||
public class ViveAnchor : OpenXRFeature
|
public class ViveAnchor : OpenXRFeature
|
||||||
{
|
{
|
||||||
public const string kOpenxrExtensionString = "XR_HTC_anchor";
|
public const string kOpenxrExtensionString = "XR_HTC_anchor XR_EXT_future XR_HTC_anchor_persistence";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string featureId = "vive.wave.openxr.feature.htcanchor";
|
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 XrInstance m_XrInstance = 0;
|
||||||
private XrSession session = 0;
|
private XrSession session = 0;
|
||||||
private XrSystemId m_XrSystemId = 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
|
#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 struct XrSystemAnchorPropertiesHTC
|
||||||
{
|
{
|
||||||
public XrStructureType type;
|
public XrStructureType type;
|
||||||
@@ -50,11 +67,35 @@ namespace VIVE.OpenXR.Anchor
|
|||||||
public XrBool32 supportsAnchor;
|
public XrBool32 supportsAnchor;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
/// <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
|
public struct XrSpatialAnchorNameHTC
|
||||||
{
|
{
|
||||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
|
||||||
public string name;
|
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 struct XrSpatialAnchorCreateInfoHTC
|
||||||
@@ -66,70 +107,191 @@ namespace VIVE.OpenXR.Anchor
|
|||||||
public XrSpatialAnchorNameHTC name;
|
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
|
#endregion
|
||||||
|
|
||||||
#region delegates and delegate instances
|
#region delegates and delegate instances
|
||||||
delegate XrResult DelegateXrCreateSpatialAnchorHTC(XrSession session, ref XrSpatialAnchorCreateInfoHTC createInfo, ref XrSpace anchor);
|
public delegate XrResult DelegateXrCreateSpatialAnchorHTC(XrSession session, ref XrSpatialAnchorCreateInfoHTC createInfo, ref XrSpace anchor);
|
||||||
delegate XrResult DelegateXrGetSpatialAnchorNameHTC(XrSpace anchor, ref XrSpatialAnchorNameHTC name);
|
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;
|
DelegateXrCreateSpatialAnchorHTC XrCreateSpatialAnchorHTC;
|
||||||
DelegateXrGetSpatialAnchorNameHTC XrGetSpatialAnchorNameHTC;
|
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
|
#endregion delegates and delegate instances
|
||||||
|
|
||||||
#region override functions
|
#region override functions
|
||||||
/// <inheritdoc />
|
|
||||||
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||||
{
|
{
|
||||||
Debug.Log("ViveAnchor HookGetInstanceProcAddr() ");
|
// For LocateSpace, need WaitFrame's predictedDisplayTime.
|
||||||
|
ViveInterceptors.Instance.AddRequiredFunction("xrWaitFrame");
|
||||||
return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
|
return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||||
{
|
{
|
||||||
//Debug.Log("VIVEAnchor OnInstanceCreate() ");
|
#if FAKE_DATA
|
||||||
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
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.LogWarning("ViveAnchor OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_XrInstance = xrInstance;
|
m_XrInstance = xrInstance;
|
||||||
//Debug.Log("OnInstanceCreate() " + m_XrInstance);
|
|
||||||
CommonWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
|
|
||||||
SpaceWrapper.Instance.OnInstanceCreate(xrInstance, CommonWrapper.Instance.GetInstanceProcAddr);
|
|
||||||
|
|
||||||
return GetXrFunctionDelegates(m_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)
|
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||||
{
|
{
|
||||||
|
m_XrInstance = 0;
|
||||||
|
|
||||||
|
IsInited = false;
|
||||||
|
IsPAInited = false;
|
||||||
|
|
||||||
CommonWrapper.Instance.OnInstanceDestroy();
|
CommonWrapper.Instance.OnInstanceDestroy();
|
||||||
SpaceWrapper.Instance.OnInstanceDestroy();
|
SpaceWrapper.Instance.OnInstanceDestroy();
|
||||||
|
FutureWrapper.Instance.OnInstanceDestroy();
|
||||||
|
Debug.Log("ViveAnchor: OnInstanceDestroy()");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnSessionCreate(ulong xrSession)
|
protected override void OnSessionCreate(ulong xrSession)
|
||||||
{
|
{
|
||||||
Debug.Log("ViveAnchor OnSessionCreate() ");
|
//Debug.Log("ViveAnchor OnSessionCreate() ");
|
||||||
|
|
||||||
// here's one way you can grab the session
|
|
||||||
Debug.Log($"EXT: Got xrSession: {xrSession}");
|
|
||||||
session = xrSession;
|
session = xrSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnSessionBegin(ulong xrSession)
|
protected override void OnSessionDestroy(ulong xrSession)
|
||||||
{
|
{
|
||||||
Debug.Log("ViveAnchor OnSessionBegin() ");
|
//Debug.Log("ViveAnchor OnSessionDestroy() ");
|
||||||
Debug.Log($"EXT: xrBeginSession: {xrSession}");
|
session = 0;
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void OnSessionEnd(ulong xrSession)
|
|
||||||
{
|
|
||||||
Debug.Log("ViveAnchor OnSessionEnd() ");
|
|
||||||
Debug.Log($"EXT: about to xrEndSession: {xrSession}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX Every millisecond the AppSpace switched from one space to another space. I don't know what is going on.
|
// XXX Every millisecond the AppSpace switched from one space to another space. I don't know what is going on.
|
||||||
@@ -144,30 +306,53 @@ namespace VIVE.OpenXR.Anchor
|
|||||||
protected override void OnSystemChange(ulong xrSystem)
|
protected override void OnSystemChange(ulong xrSystem)
|
||||||
{
|
{
|
||||||
m_XrSystemId = xrSystem;
|
m_XrSystemId = xrSystem;
|
||||||
Debug.Log("ViveAnchor OnSystemChange() " + m_XrSystemId);
|
//Debug.Log("ViveAnchor OnSystemChange() " + m_XrSystemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endregion override functions
|
#endregion override functions
|
||||||
|
|
||||||
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
private bool GetXrFunctionDelegates(XrInstance inst)
|
||||||
{
|
{
|
||||||
Debug.Log("ViveAnchor GetXrFunctionDelegates() ");
|
Debug.Log("ViveAnchor GetXrFunctionDelegates() ");
|
||||||
|
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
IntPtr funcPtr = IntPtr.Zero;
|
|
||||||
OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr = CommonWrapper.Instance.GetInstanceProcAddr; // shorter name
|
OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr = CommonWrapper.Instance.GetInstanceProcAddr; // shorter name
|
||||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrCreateSpatialAnchorHTC", out XrCreateSpatialAnchorHTC);
|
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrCreateSpatialAnchorHTC", out XrCreateSpatialAnchorHTC);
|
||||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrGetSpatialAnchorNameHTC", out XrGetSpatialAnchorNameHTC);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region functions of extension
|
#region functions of extension
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper function to get this feature' properties.
|
/// 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>
|
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="anchorProperties">Output parameter to hold anchor properties.</param>
|
||||||
|
/// <returns>XrResult indicating success or failure.</returns>
|
||||||
public XrResult GetProperties(out XrSystemAnchorPropertiesHTC anchorProperties)
|
public XrResult GetProperties(out XrSystemAnchorPropertiesHTC anchorProperties)
|
||||||
{
|
{
|
||||||
anchorProperties = new XrSystemAnchorPropertiesHTC();
|
anchorProperties = new XrSystemAnchorPropertiesHTC();
|
||||||
@@ -184,34 +369,293 @@ namespace VIVE.OpenXR.Anchor
|
|||||||
return CommonWrapper.Instance.GetProperties(m_XrInstance, m_XrSystemId, ref anchorProperties);
|
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)
|
public XrResult CreateSpatialAnchor(XrSpatialAnchorCreateInfoHTC createInfo, out XrSpace anchor)
|
||||||
{
|
{
|
||||||
anchor = default;
|
anchor = default;
|
||||||
#if FAKE_DATA
|
if (!IsInited)
|
||||||
if (Application.isEditor)
|
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
return XrResult.XR_SUCCESS;
|
if (session == 0)
|
||||||
#endif
|
return XrResult.XR_ERROR_SESSION_LOST;
|
||||||
|
|
||||||
var ret = XrCreateSpatialAnchorHTC(session, ref createInfo, ref anchor);
|
var ret = XrCreateSpatialAnchorHTC(session, ref createInfo, ref anchor);
|
||||||
Debug.Log("ViveAnchor CreateSpatialAnchor() r=" + ret + ", a=" + anchor + ", bs=" + createInfo.space +
|
//Debug.Log("ViveAnchor CreateSpatialAnchor() r=" + ret + ", a=" + anchor + ", bs=" + createInfo.space +
|
||||||
", pos=(" + createInfo.poseInSpace.position.x + "," + createInfo.poseInSpace.position.y + "," + createInfo.poseInSpace.position.z +
|
// ", 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 +
|
// "), rot=(" + createInfo.poseInSpace.orientation.x + "," + createInfo.poseInSpace.orientation.y + "," + createInfo.poseInSpace.orientation.z + "," + createInfo.poseInSpace.orientation.w +
|
||||||
"), n=" + createInfo.name.name);
|
// "), n=" + createInfo.name.name);
|
||||||
return ret;
|
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)
|
public XrResult GetSpatialAnchorName(XrSpace anchor, out XrSpatialAnchorNameHTC name)
|
||||||
{
|
{
|
||||||
name = default;
|
name = new XrSpatialAnchorNameHTC();
|
||||||
#if FAKE_DATA
|
if (!IsInited)
|
||||||
if (Application.isEditor)
|
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
{
|
|
||||||
name.name = "fake anchor";
|
|
||||||
return XrResult.XR_SUCCESS;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return XrGetSpatialAnchorNameHTC(anchor, ref name);
|
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
|
#endregion
|
||||||
|
|
||||||
#region tools for user
|
#region tools for user
|
||||||
@@ -223,7 +667,7 @@ namespace VIVE.OpenXR.Anchor
|
|||||||
public XrSpace GetTrackingSpace()
|
public XrSpace GetTrackingSpace()
|
||||||
{
|
{
|
||||||
var s = GetCurrentAppSpace();
|
var s = GetCurrentAppSpace();
|
||||||
Debug.Log("ViveAnchor GetTrackingSpace() s=" + s);
|
//Debug.Log("ViveAnchor GetTrackingSpace() s=" + s);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine.XR.OpenXR;
|
||||||
|
using UnityEngine.XR.OpenXR.Features;
|
||||||
|
using AOT;
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor.XR.OpenXR.Features;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.CompositionLayer
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[OpenXRFeature(UiName = "VIVE XR Composition Layer (Equirect)",
|
||||||
|
Desc = "Enable this feature to enable the Composition Layer Equirect Extension",
|
||||||
|
Company = "HTC",
|
||||||
|
DocumentationLink = "..\\Documentation",
|
||||||
|
OpenxrExtensionStrings = kOpenXRCylinderExtensionString,
|
||||||
|
Version = "1.0.0",
|
||||||
|
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||||
|
FeatureId = featureId
|
||||||
|
)]
|
||||||
|
#endif
|
||||||
|
public class ViveCompositionLayerEquirect : OpenXRFeature
|
||||||
|
{
|
||||||
|
const string LOG_TAG = "VIVE.OpenXR.ViveCompositionLayer.Equirect";
|
||||||
|
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>
|
||||||
|
/// 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.equirect";
|
||||||
|
|
||||||
|
private const string kOpenXRCylinderExtensionString = "XR_KHR_composition_layer_equirect XR_KHR_composition_layer_equirect2";
|
||||||
|
|
||||||
|
private bool m_EquirectExtensionEnabled = true;
|
||||||
|
/// <summary>
|
||||||
|
/// The extension "XR_KHR_composition_layer_equirect" is enabled or not.
|
||||||
|
/// </summary>
|
||||||
|
public bool EquirectExtensionEnabled
|
||||||
|
{
|
||||||
|
get { return m_EquirectExtensionEnabled; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool m_Equirect2ExtensionEnabled = true;
|
||||||
|
/// <summary>
|
||||||
|
/// The extension "XR_KHR_composition_layer_equirect2" is enabled or not.
|
||||||
|
/// </summary>
|
||||||
|
public bool Equirect2ExtensionEnabled
|
||||||
|
{
|
||||||
|
get { return m_Equirect2ExtensionEnabled; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region OpenXR Life Cycle
|
||||||
|
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||||
|
{
|
||||||
|
if (!OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_equirect"))
|
||||||
|
{
|
||||||
|
WARNING("OnInstanceCreate() " + "XR_KHR_composition_layer_equirect" + " is NOT enabled.");
|
||||||
|
|
||||||
|
m_EquirectExtensionEnabled = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_equirect2"))
|
||||||
|
{
|
||||||
|
WARNING("OnInstanceCreate() " + "XR_KHR_composition_layer_equirect2" + " is NOT enabled.");
|
||||||
|
|
||||||
|
m_Equirect2ExtensionEnabled = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Wrapper Functions
|
||||||
|
private const string ExtLib = "viveopenxr";
|
||||||
|
|
||||||
|
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerEquirect")]
|
||||||
|
public static extern void VIVEOpenXR_Submit_CompositionLayerEquirect(XrCompositionLayerEquirectKHR equirect, LayerType layerType, uint compositionDepth, int layerID);
|
||||||
|
/// <summary>
|
||||||
|
/// submit compostion layer of type equirect.
|
||||||
|
/// </summary>
|
||||||
|
public void Submit_CompositionLayerEquirect(XrCompositionLayerEquirectKHR equirect, LayerType layerType, uint compositionDepth, int layerID)
|
||||||
|
{
|
||||||
|
if (!EquirectExtensionEnabled)
|
||||||
|
{
|
||||||
|
ERROR("Submit_CompositionLayerEquirect: " + "XR_KHR_composition_layer_equirect" + " is NOT enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
VIVEOpenXR_Submit_CompositionLayerEquirect(equirect, layerType, compositionDepth, layerID);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerEquirect2")]
|
||||||
|
public static extern void VIVEOpenXR_Submit_CompositionLayerEquirect2(XrCompositionLayerEquirect2KHR equirect2, LayerType layerType, uint compositionDepth, int layerID);
|
||||||
|
/// <summary>
|
||||||
|
/// submit compostion layer of type equirect2.
|
||||||
|
/// </summary>
|
||||||
|
public void Submit_CompositionLayerEquirect2(XrCompositionLayerEquirect2KHR equirect2, LayerType layerType, uint compositionDepth, int layerID)
|
||||||
|
{
|
||||||
|
if (!Equirect2ExtensionEnabled)
|
||||||
|
{
|
||||||
|
ERROR("Submit_CompositionLayerEquirect2: " + "XR_KHR_composition_layer_equirect2" + " is NOT enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
VIVEOpenXR_Submit_CompositionLayerEquirect2(equirect2, layerType, compositionDepth, layerID);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ec826264ff4d75d4081f2ca472a3e083
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f26de592e4135874baf6e64cc94183be
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright HTC Corporation All Rights Reserved.
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
using System;
|
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>
|
||||||
{
|
{
|
||||||
@@ -274,6 +215,35 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
public float aspectRatio;
|
public float aspectRatio;
|
||||||
}
|
}
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrCompositionLayerEquirectKHR
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public XrCompositionLayerFlags layerFlags;
|
||||||
|
public XrSpace space;
|
||||||
|
public XrEyeVisibility eyeVisibility;
|
||||||
|
public XrSwapchainSubImage subImage;
|
||||||
|
public XrPosef pose;
|
||||||
|
public float radius;
|
||||||
|
public XrVector2f scale;
|
||||||
|
public XrVector2f bias;
|
||||||
|
}
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrCompositionLayerEquirect2KHR
|
||||||
|
{
|
||||||
|
public XrStructureType type;
|
||||||
|
public IntPtr next;
|
||||||
|
public XrCompositionLayerFlags layerFlags;
|
||||||
|
public XrSpace space;
|
||||||
|
public XrEyeVisibility eyeVisibility;
|
||||||
|
public XrSwapchainSubImage subImage;
|
||||||
|
public XrPosef pose;
|
||||||
|
public float radius;
|
||||||
|
public float centralHorizontalAngle;
|
||||||
|
public float upperVerticalAngle;
|
||||||
|
public float lowerVerticalAngle;
|
||||||
|
}
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct XrSwapchainSubImage
|
public struct XrSwapchainSubImage
|
||||||
{
|
{
|
||||||
public XrSwapchain swapchain;
|
public XrSwapchain swapchain;
|
||||||
@@ -288,6 +258,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,
|
||||||
@@ -410,29 +410,6 @@ namespace VIVE.OpenXR.CompositionLayer
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The XrCompositionLayerBaseHeader structure is not intended to be directly used, but forms a basis for defining current and future structures containing composition layer information. The XrFrameEndInfo structure contains an array of pointers to these polymorphic header structures.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct XrCompositionLayerBaseHeader
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The XrStructureType of this structure.
|
|
||||||
/// </summary>
|
|
||||||
public XrStructureType type;
|
|
||||||
/// <summary>
|
|
||||||
/// Next is NULL or a pointer to the next structure in a structure chain, such as XrPassthroughMeshTransformInfoHTC.
|
|
||||||
/// </summary>
|
|
||||||
public IntPtr next;
|
|
||||||
/// <summary>
|
|
||||||
/// A bitmask of XrCompositionLayerFlagBits describing flags to apply to the layer.
|
|
||||||
/// </summary>
|
|
||||||
public XrCompositionLayerFlags layerFlags;
|
|
||||||
/// <summary>
|
|
||||||
/// The XrSpace in which the layer will be kept stable over time.
|
|
||||||
/// </summary>
|
|
||||||
public XrSpace space;
|
|
||||||
};
|
|
||||||
/// <summary>
|
|
||||||
/// The application can specify the XrPassthroughColorHTC to adjust the alpha value of the passthrough. The range is between 0.0f and 1.0f, 1.0f means opaque.
|
/// 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>
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ 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",
|
||||||
@@ -28,6 +28,7 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
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";
|
||||||
@@ -527,7 +528,10 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
|
|
||||||
XrResult res = xrCreatePassthroughHTC(m_XrSession, createInfo, out passthrough);
|
XrResult res = xrCreatePassthroughHTC(m_XrSession, createInfo, out passthrough);
|
||||||
if (res == XrResult.XR_SUCCESS)
|
if (res == XrResult.XR_SUCCESS)
|
||||||
|
{
|
||||||
passthroughList.Add(passthrough);
|
passthroughList.Add(passthrough);
|
||||||
|
passthroughIDList.Add(((int)(ulong)passthrough));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ERROR("CreatePassthroughHTC() "+res);
|
ERROR("CreatePassthroughHTC() "+res);
|
||||||
return res;
|
return res;
|
||||||
@@ -541,6 +545,7 @@ namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
|||||||
if (res == XrResult.XR_SUCCESS)
|
if (res == XrResult.XR_SUCCESS)
|
||||||
{
|
{
|
||||||
passthroughList.Remove(passthrough);
|
passthroughList.Remove(passthrough);
|
||||||
|
passthroughIDList.Remove(((int)(ulong)passthrough));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 146db425ea37c2746ad7c9ae08a5a480
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
com.htc.upm.vive.openxr/Runtime/Features/EyeTracker.meta
Normal file
8
com.htc.upm.vive.openxr/Runtime/Features/EyeTracker.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 65a24dbb45287c244bce088cb4a0a8aa
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,676 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
using UnityEngine.XR.OpenXR;
|
||||||
|
using UnityEngine.XR.OpenXR.Features;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System;
|
||||||
|
using AOT;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.XR.OpenXR.Features;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.EyeTracker
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[OpenXRFeature(UiName = "VIVE XR Eye Tracker (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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9eca1674b64bae840af3a53d3ae576ec
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.EyeTracker
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyeTrackerHTC handle represents the resources for eye tracking.
|
||||||
|
/// </summary>
|
||||||
|
public struct XrEyeTrackerHTC : IEquatable<UInt64>
|
||||||
|
{
|
||||||
|
private readonly UInt64 value;
|
||||||
|
|
||||||
|
public XrEyeTrackerHTC(UInt64 u)
|
||||||
|
{
|
||||||
|
value = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator UInt64(XrEyeTrackerHTC equatable)
|
||||||
|
{
|
||||||
|
return equatable.value;
|
||||||
|
}
|
||||||
|
public static implicit operator XrEyeTrackerHTC(UInt64 u)
|
||||||
|
{
|
||||||
|
return new XrEyeTrackerHTC(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(XrEyeTrackerHTC other)
|
||||||
|
{
|
||||||
|
return value == other.value;
|
||||||
|
}
|
||||||
|
public bool Equals(UInt64 other)
|
||||||
|
{
|
||||||
|
return value == other;
|
||||||
|
}
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is XrEyeTrackerHTC && Equals((XrEyeTrackerHTC)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return value.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.Equals(b); }
|
||||||
|
public static bool operator !=(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return !a.Equals(b); }
|
||||||
|
public static bool operator >=(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value >= b.value; }
|
||||||
|
public static bool operator <=(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value <= b.value; }
|
||||||
|
public static bool operator >(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value > b.value; }
|
||||||
|
public static bool operator <(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value < b.value; }
|
||||||
|
public static XrEyeTrackerHTC operator +(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value + b.value; }
|
||||||
|
public static XrEyeTrackerHTC operator -(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value - b.value; }
|
||||||
|
public static XrEyeTrackerHTC operator *(XrEyeTrackerHTC a, XrEyeTrackerHTC b) { return a.value * b.value; }
|
||||||
|
public static XrEyeTrackerHTC operator /(XrEyeTrackerHTC a, XrEyeTrackerHTC b)
|
||||||
|
{
|
||||||
|
if (b.value == 0)
|
||||||
|
{
|
||||||
|
throw new DivideByZeroException();
|
||||||
|
}
|
||||||
|
return a.value / b.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyePositionHTC describes which eye is under tracking for the data retrieved from <see cref="XrEyeGazeDataHTC">XrEyeGazeDataHTC</see>, <see cref="XrEyePupilDataHTC">XrEyePupilDataHTC</see> or <see cref="XrEyeGeometricDataHTC">XrEyeGeometricDataHTC</see>.
|
||||||
|
/// </summary>
|
||||||
|
public enum XrEyePositionHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the position of the left eye.
|
||||||
|
/// </summary>
|
||||||
|
XR_EYE_POSITION_LEFT_HTC = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the position of the right eye.
|
||||||
|
/// </summary>
|
||||||
|
XR_EYE_POSITION_RIGHT_HTC = 1,
|
||||||
|
XR_EYE_POSITION_COUNT_HTC = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An application can inspect whether the system is capable of eye tracking input by extending the <see cref="XrSystemProperties">XrSystemProperties</see> with <see cref="XrSystemEyeTrackingPropertiesHTC">XrSystemEyeTrackingPropertiesHTC</see> structure when calling <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrSystemEyeTrackingPropertiesHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
/// <summary>
|
||||||
|
/// Indicating if the current system is capable of receiving eye tracking input.
|
||||||
|
/// </summary>
|
||||||
|
public XrBool32 supportsEyeTracking;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyeTrackerCreateInfoHTC structure describes the information to create an <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> handle.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyeTrackerCreateInfoHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
|
||||||
|
/// <param name="in_type">The <see cref="XrStructureType">XrStructureType</see> of this structure.</param>
|
||||||
|
/// <param name="in_next">NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.</param>
|
||||||
|
public XrEyeTrackerCreateInfoHTC(XrStructureType in_type, IntPtr in_next)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyeGazeDataInfoHTC structure describes the information to get eye gaze directions.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyeGazeDataInfoHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrSpace">XrSpace</see> within which the returned eye poses will be represented.
|
||||||
|
/// </summary>
|
||||||
|
public XrSpace baseSpace;
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrTime">XrTime</see> at which the eye gaze information is requested.
|
||||||
|
/// </summary>
|
||||||
|
public XrTime time;
|
||||||
|
|
||||||
|
/// <param name="in_type">The <see cref="XrStructureType">XrStructureType</see> of this structure.</param>
|
||||||
|
/// <param name="in_next">NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.</param>
|
||||||
|
/// <param name="in_baseSpace">An <see cref="XrSpace">XrSpace</see> within which the returned eye poses will be represented.</param>
|
||||||
|
/// <param name="in_time">An <see cref="XrTime">XrTime</see> at which the eye gaze information is requested.</param>
|
||||||
|
public XrEyeGazeDataInfoHTC(XrStructureType in_type, IntPtr in_next, XrSpace in_baseSpace, XrTime in_time)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
baseSpace = in_baseSpace;
|
||||||
|
time = in_time;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrSingleEyeGazeDataHTC structure describes the validity and direction of a eye gaze observation.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrSingleEyeGazeDataHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrBool32">XrBool32</see> indicating if the returned gazePose is valid. Callers should check the validity of pose prior to use.
|
||||||
|
/// </summary>
|
||||||
|
public XrBool32 isValid;
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrPosef">XrPosef</see> describing the position and orientation of the user's eye. The pose is represented in the coordinate system provided by <see cref="XrEyeGazeDataInfoHTC">XrEyeGazeDataInfoHTC</see>::<see cref="XrEyeGazeDataInfoHTC.baseSpace">baseSpace</see>.
|
||||||
|
/// </summary>
|
||||||
|
public XrPosef gazePose;
|
||||||
|
|
||||||
|
/// <param name="in_isValid">An <see cref="XrBool32">XrBool32</see> indicating if the returned gazePose is valid. Callers should check the validity of pose prior to use.</param>
|
||||||
|
/// <param name="in_gazePose">An <see cref="XrPosef">XrPosef</see> describing the position and orientation of the user's eye. The pose is represented in the coordinate system provided by <see cref="XrEyeGazeDataInfoHTC">XrEyeGazeDataInfoHTC</see>::<see cref="XrEyeGazeDataInfoHTC.baseSpace">baseSpace</see>.</param>
|
||||||
|
public XrSingleEyeGazeDataHTC(XrBool32 in_isValid, XrPosef in_gazePose)
|
||||||
|
{
|
||||||
|
isValid = in_isValid;
|
||||||
|
gazePose = in_gazePose;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyeGazeDataHTC structure returns the state of the eye gaze directions.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyeGazeDataHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrTime">XrTime</see> at which the eye gaze information is requested.
|
||||||
|
/// </summary>
|
||||||
|
public XrTime time;
|
||||||
|
/// <summary>
|
||||||
|
/// An array of <see cref="XrSingleEyeGazeDataHTC">XrSingleEyeGazeDataHTC</see> receiving the returned eye gaze directions.
|
||||||
|
/// </summary>
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||||
|
public XrSingleEyeGazeDataHTC[] gaze;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyePupilDataInfoHTC structure describes the information to get pupil data.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyePupilDataInfoHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
/// <param name="in_type">The <see cref="XrStructureType">XrStructureType</see> of this structure.</param>
|
||||||
|
/// <param name="in_next">NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.</param>
|
||||||
|
public XrEyePupilDataInfoHTC(XrStructureType in_type, IntPtr in_next)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrSingleEyePupilDataHTC structure describes the validity, diameter and position of a pupil observation.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrSingleEyePupilDataHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrBool32">XrBool32</see> indicating if the returned pupilDiameter is valid. Callers should check the validity of diameter prior to use.
|
||||||
|
/// </summary>
|
||||||
|
public XrBool32 isDiameterValid;
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrBool32">XrBool32</see> indicating if the returned pupilPosition is valid. Callers should check the validity of position prior to use.
|
||||||
|
/// </summary>
|
||||||
|
public XrBool32 isPositionValid;
|
||||||
|
/// <summary>
|
||||||
|
/// The diameter of pupil in millimeters.
|
||||||
|
/// </summary>
|
||||||
|
public float pupilDiameter;
|
||||||
|
/// <summary>
|
||||||
|
/// The position of pupil in sensor area which x and y are normalized in [0,1] with +Y up and +X to the right.
|
||||||
|
/// </summary>
|
||||||
|
public XrVector2f pupilPosition;
|
||||||
|
|
||||||
|
/// <param name="in_isDiameterValid">An <see cref="XrBool32">XrBool32</see> indicating if the returned gazePose is valid. Callers should check the validity of pose prior to use.</param>
|
||||||
|
/// <param name="in_isPositionValid">An <see cref="XrBool32">XrBool32</see> indicating if the returned pupilPosition is valid. Callers should check the validity of position prior to use.</param>
|
||||||
|
/// <param name="in_pupilDiameter">The diameter of pupil in millimeters.</param>
|
||||||
|
/// <param name="in_pupilPosition">The position of pupil in sensor area which x and y are normalized in [0,1]with +Y up and +X to the right.</param>
|
||||||
|
public XrSingleEyePupilDataHTC(XrBool32 in_isDiameterValid, XrBool32 in_isPositionValid, float in_pupilDiameter, XrVector2f in_pupilPosition)
|
||||||
|
{
|
||||||
|
isDiameterValid = in_isDiameterValid;
|
||||||
|
isPositionValid = in_isPositionValid;
|
||||||
|
pupilDiameter = in_pupilDiameter;
|
||||||
|
pupilPosition = in_pupilPosition;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyePupilDataHTC structure returns the pupil data.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyePupilDataHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrTime">XrTime</see> at which the pupil data was captured.
|
||||||
|
/// </summary>
|
||||||
|
public XrTime time;
|
||||||
|
/// <summary>
|
||||||
|
/// An array of <see cref="XrSingleEyePupilDataHTC">XrSingleEyePupilDataHTC</see> receiving the returned pupil data.
|
||||||
|
/// </summary>
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||||
|
public XrSingleEyePupilDataHTC[] pupilData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrEyeGeometricDataInfoHTC structure describes the information to get geometric related data.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyeGeometricDataInfoHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
/// <param name="in_type">The <see cref="XrStructureType">XrStructureType</see> of this structure.</param>
|
||||||
|
/// <param name="in_next">NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.</param>
|
||||||
|
public XrEyeGeometricDataInfoHTC(XrStructureType in_type, IntPtr in_next)
|
||||||
|
{
|
||||||
|
type = in_type;
|
||||||
|
next = in_next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The XrSingleEyeGeometricDataHTC structure describes the geometric related data.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrSingleEyeGeometricDataHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A flag that indicates if the geometric data is valid. Callers should check the validity of the geometric data prior to use.
|
||||||
|
/// </summary>
|
||||||
|
public XrBool32 isValid;
|
||||||
|
/// <summary>
|
||||||
|
/// A value in range [0,1] representing the openness of the user's eye. When this value is zero, the eye closes normally. When this value is one, the eye opens normally. When this value goes higher, the eye approaches open.
|
||||||
|
/// </summary>
|
||||||
|
public float eyeOpenness;
|
||||||
|
/// <summary>
|
||||||
|
/// A value in range [0,1] representing how the user's eye open widely. When this value is zero, the eye opens normally. When this value goes higher, the eye opens wider.
|
||||||
|
public float eyeWide;
|
||||||
|
/// <summary>
|
||||||
|
/// A value in range [0,1] representing how the user's eye is closed. When this value is zero, the eye closes normally. When this value goes higher, the eye closes tighter.
|
||||||
|
/// </summary>
|
||||||
|
public float eyeSqueeze;
|
||||||
|
|
||||||
|
/// <param name="in_isValid">A flag that indicates if the geometric data is valid. Callers should check the validity of the geometric data prior to use.</param>
|
||||||
|
/// <param name="in_eyeOpenness">A value in range [0,1] representing the openness of the user's eye. When this value is zero, the eye closes normally. When this value is one, the eye opens normally. When this value goes higher, the eye approaches open.</param>
|
||||||
|
/// <param name="in_eyeWide">A value in range [0,1] representing how the user's eye open widely. When this value is zero, the eye opens normally. When this value goes higher, the eye opens wider.</param>
|
||||||
|
/// <param name="in_eyeSqueeze">A value in range [0,1] representing how the user's eye is closed. When this value is zero, the eye closes normally. When this value goes higher, the eye closes tighter.</param>
|
||||||
|
public XrSingleEyeGeometricDataHTC(XrBool32 in_isValid, float in_eyeOpenness, float in_eyeWide, float in_eyeSqueeze)
|
||||||
|
{
|
||||||
|
isValid = in_isValid;
|
||||||
|
eyeOpenness = in_eyeOpenness;
|
||||||
|
eyeWide = in_eyeWide;
|
||||||
|
eyeSqueeze = in_eyeSqueeze;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct XrEyeGeometricDataHTC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
|
||||||
|
/// </summary>
|
||||||
|
public XrStructureType type;
|
||||||
|
/// <summary>
|
||||||
|
/// NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr next;
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="XrTime">XrTime</see> at which the returned eye data is tracked.
|
||||||
|
/// </summary>
|
||||||
|
public XrTime time;
|
||||||
|
/// <summary>
|
||||||
|
/// An array of <see cref="XrSingleEyeGeometricDataHTC">XrSingleEyeGeometricDataHTC</see> receiving the returned eye geometric data.
|
||||||
|
/// </summary>
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||||
|
public XrSingleEyeGeometricDataHTC[] eyeGeometricData;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static class ViveEyeTrackerHelper
|
||||||
|
{
|
||||||
|
/// <param name="session">An XrSession in which the eye tracker will be active.</param>
|
||||||
|
/// <param name="createInfo">The <see cref="XrEyeTrackerCreateInfoHTC">XrEyeTrackerCreateInfoHTC</see> used to specify the eye tracker.</param>
|
||||||
|
/// <param name="eyeTracker">The returned <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> handle.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public delegate XrResult xrCreateEyeTrackerHTCDelegate(
|
||||||
|
XrSession session,
|
||||||
|
ref XrEyeTrackerCreateInfoHTC createInfo,
|
||||||
|
out XrEyeTrackerHTC eyeTracker);
|
||||||
|
|
||||||
|
/// <param name="eyeTracker">An XrEyeTrackerHTC previously created by xrCreateEyeTrackerHTC.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public delegate XrResult xrDestroyEyeTrackerHTCDelegate(
|
||||||
|
XrEyeTrackerHTC eyeTracker);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="XrEyeGazeDataHTC">XrEyeGazeDataHTC</see> data of a <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="gazeInfo">The information to get eye gaze.</param>
|
||||||
|
/// <param name="eyeGazes">A pointer to <see cref="XrEyeGazeDataHTC">XrEyeGazeDataHTC</see> receiving the returned eye poses.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public delegate XrResult xrGetEyeGazeDataHTCDelegate(
|
||||||
|
XrEyeTrackerHTC eyeTracker,
|
||||||
|
ref XrEyeGazeDataInfoHTC gazeInfo,
|
||||||
|
ref XrEyeGazeDataHTC eyeGazes);
|
||||||
|
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="pupilDataInfo">The information to get pupil data.</param>
|
||||||
|
/// <param name="pupilData">A pointer to <see cref="XrEyePupilDataHTC">XrEyePupilDataHTC</see> returned by the runtime.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public delegate XrResult xrGetEyePupilDataHTCDelegate(
|
||||||
|
XrEyeTrackerHTC eyeTracker,
|
||||||
|
ref XrEyePupilDataInfoHTC pupilDataInfo,
|
||||||
|
ref XrEyePupilDataHTC pupilData);
|
||||||
|
|
||||||
|
/// <param name="eyeTracker">An <see cref="XrEyeTrackerHTC">XrEyeTrackerHTC</see> previously created by <see cref="xrCreateEyeTrackerHTCDelegate">xrCreateEyeTrackerHTC</see>.</param>
|
||||||
|
/// <param name="info">A pointer to <see cref="XrEyeGeometricDataInfoHTC">XrEyeGeometricDataInfoHTC</see> structure.</param>
|
||||||
|
/// <param name="eyeGeometricData">A pointer to <see cref="XrEyeGeometricDataHTC">XrEyeGeometricDataHTC</see> returned by the runtime.</param>
|
||||||
|
/// <returns>XR_SUCCESS for success.</returns>
|
||||||
|
public delegate XrResult xrGetEyeGeometricDataHTC(
|
||||||
|
XrEyeTrackerHTC eyeTracker,
|
||||||
|
ref XrEyeGeometricDataInfoHTC info,
|
||||||
|
ref XrEyeGeometricDataHTC eyeGeometricData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e9a5198e29bcef243bf89dc19b46ce0d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6368702137725614d8d921ef6c1220f1
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e80a989be51974a4e88bdc41872d53c9
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 311462c0560d6ec4ea9ed080a6a77a3b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -85,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) {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright HTC Corporation All Rights Reserved.
|
||||||
|
|
||||||
|
namespace VIVE.OpenXR.Foveation
|
||||||
|
{
|
||||||
|
#region 12.86. XR_HTC_foveation
|
||||||
|
/// <summary>
|
||||||
|
/// The XrFoveationModeHTC identifies the different foveation modes.
|
||||||
|
/// </summary>
|
||||||
|
public enum XrFoveationModeHTC
|
||||||
|
{
|
||||||
|
XR_FOVEATION_MODE_DISABLE_HTC = 0,
|
||||||
|
XR_FOVEATION_MODE_FIXED_HTC = 1,
|
||||||
|
XR_FOVEATION_MODE_DYNAMIC_HTC = 2,
|
||||||
|
XR_FOVEATION_MODE_CUSTOM_HTC = 3,
|
||||||
|
XR_FOVEATION_MODE_MAX_ENUM_HTC = 0x7FFFFFFF
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The XrFoveationLevelHTC identifies the pixel density drop level of periphery area.
|
||||||
|
/// </summary>
|
||||||
|
public enum XrFoveationLevelHTC
|
||||||
|
{
|
||||||
|
XR_FOVEATION_LEVEL_NONE_HTC = 0,
|
||||||
|
XR_FOVEATION_LEVEL_LOW_HTC = 1,
|
||||||
|
XR_FOVEATION_LEVEL_MEDIUM_HTC = 2,
|
||||||
|
XR_FOVEATION_LEVEL_HIGH_HTC = 3,
|
||||||
|
XR_FOVEATION_LEVEL_MAX_ENUM_HTC = 0x7FFFFFFF
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The XrFoveationConfigurationHTC structure contains the custom foveation settings for the corresponding views.
|
||||||
|
/// </summary>
|
||||||
|
public struct XrFoveationConfigurationHTC
|
||||||
|
{
|
||||||
|
public XrFoveationLevelHTC level;
|
||||||
|
public float clearFovDegree;
|
||||||
|
public XrVector2f focalCenterOffset;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6b3c2ad651da4e5498f49d3a26038620
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 83e064b5ad501784c898651afc560f8e
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 73f495f4b0dd14245b3997ffbe23713a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# 12.1. XR_HTC_frame_synchronization
|
||||||
|
## Overview
|
||||||
|
The application frame loop relies on xrWaitFrame throttling to synchronize application frame submissions with the display. This extension allows the application to set the frame synchronization mode to adjust the interval between the application frame submission time and the corresponding display time according to the demand of the application. The runtime will return the appropriate XrFrameState::predictedDisplayTime returned by xrWaitFrame to throttle the frame loop approaching to the frame rendering time of the application with the consistent good user experience throughout the session.
|
||||||
|
## Name String
|
||||||
|
XR_HTC_frame_synchronization
|
||||||
|
## Revision
|
||||||
|
1
|
||||||
|
## New Enum Constants
|
||||||
|
[XrStructureType](https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XrStructureType) enumeration is extended with:
|
||||||
|
- XR_TYPE_FRAME_SYNCHRONIZATION_SESSION_BEGIN_INFO_HTC
|
||||||
|
## New Enums
|
||||||
|
- XrFrameSynchronizationModeHTC
|
||||||
|
## New Structures
|
||||||
|
- XrFrameSynchronizationSessionBeginInfoHTC
|
||||||
|
|
||||||
|
## VIVE Plugin
|
||||||
|
|
||||||
|
Enable "VIVE XR Frame Synchronization" in "Project Settings > XR Plugin-in Management > OpenXR > Android Tab > OpenXR Feature Groups" to use the frame synchronization provided by VIVE OpenXR plugin.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 190a1897e332b7f45893a24c3f696567
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user