上传YomovSDK
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95fd3a5ad5d72ba468bdcde07d5afc42
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using UnityEditor;
|
||||
using Unity.XR.CompositionLayers.Services;
|
||||
using UnityEngine.XR.OpenXR.CompositionLayers;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.CompositionLayers
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables OpenXR Composition Layers.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = FeatureName,
|
||||
Desc = "Necessary to use OpenXR Composition Layers.",
|
||||
Company = "Unity",
|
||||
OpenxrExtensionStrings = "XR_KHR_composition_layer_cylinder XR_KHR_composition_layer_equirect XR_KHR_composition_layer_equirect2 XR_KHR_composition_layer_cube XR_KHR_composition_layer_color_scale_bias XR_KHR_android_surface_swapchain",
|
||||
Version = "1.0.0",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone, BuildTargetGroup.WSA },
|
||||
FeatureId = FeatureId
|
||||
)]
|
||||
#endif
|
||||
public class OpenXRCompositionLayersFeature : OpenXRFeature
|
||||
{
|
||||
public const string FeatureId = "com.unity.openxr.feature.compositionlayers";
|
||||
internal const string FeatureName = "Composition Layers Support";
|
||||
|
||||
protected internal override void OnSessionBegin(ulong xrSession)
|
||||
{
|
||||
if (CompositionLayerManager.Instance != null)
|
||||
{
|
||||
CompositionLayerManager.Instance.LayerProvider ??= new OpenXRLayerProvider();
|
||||
}
|
||||
}
|
||||
|
||||
protected internal override void OnSessionEnd(ulong xrSession)
|
||||
{
|
||||
if (CompositionLayerManager.Instance?.LayerProvider is OpenXRLayerProvider)
|
||||
{
|
||||
((OpenXRLayerProvider)CompositionLayerManager.Instance.LayerProvider).Dispose();
|
||||
CompositionLayerManager.Instance.LayerProvider = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6096094d69d82cc40947df806f66ab34
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,86 @@
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using System.Collections.Generic;
|
||||
using Unity.XR.CompositionLayers;
|
||||
using Unity.XR.CompositionLayers.Extensions;
|
||||
using Unity.XR.CompositionLayers.Layers;
|
||||
using Unity.XR.CompositionLayers.Services;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.CompositionLayers
|
||||
{
|
||||
internal class OpenXRCubeLayer : OpenXRCustomLayerHandler<XrCompositionLayerCubeKHR>
|
||||
{
|
||||
public static bool ExtensionEnabled = OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_cube");
|
||||
|
||||
protected override unsafe bool CreateSwapchain(CompositionLayerManager.LayerInfo layerInfo, out SwapchainCreateInfo swapchainCreateInfo)
|
||||
{
|
||||
TexturesExtension texture = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texture == null || texture.enabled == false || texture.LeftTexture == null)
|
||||
{
|
||||
swapchainCreateInfo = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
swapchainCreateInfo = new XrSwapchainCreateInfo()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Swapchain),
|
||||
CreateFlags = 0,
|
||||
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
|
||||
Format = OpenXRLayerUtility.GetDefaultColorFormat(),
|
||||
SampleCount = 1,
|
||||
Width = (uint)(texture.LeftTexture.width),
|
||||
Height = (uint)(texture.LeftTexture.height),
|
||||
FaceCount = 6,
|
||||
ArraySize = 1,
|
||||
MipCount = (uint)texture.LeftTexture.mipmapCount,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override unsafe bool CreateNativeLayer(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerCubeKHR nativeLayer)
|
||||
{
|
||||
var data = layerInfo.Layer.LayerData as CubeProjectionLayerData;
|
||||
var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
|
||||
nativeLayer = new XrCompositionLayerCubeKHR()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_CUBE_KHR,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer),
|
||||
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
|
||||
Space = OpenXRLayerUtility.GetCurrentAppSpace(),
|
||||
EyeVisibility = 0,
|
||||
Swapchain = swapchainOutput.handle,
|
||||
ImageArrayIndex = 0,
|
||||
Orientation = new XrQuaternionf(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation)
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerCubeKHR nativeLayer)
|
||||
{
|
||||
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
|
||||
return false;
|
||||
|
||||
var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
nativeLayer.Space = OpenXRLayerUtility.GetCurrentAppSpace();
|
||||
nativeLayer.Orientation = new XrQuaternionf(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerCubeKHR nativeLayer)
|
||||
{
|
||||
nativeLayer.Space = OpenXRLayerUtility.GetCurrentAppSpace();
|
||||
return base.ActiveNativeLayer(layerInfo, ref nativeLayer);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb695c3684cd4e948bac4768c2e36307
|
||||
timeCreated: 1693948528
|
||||
@@ -0,0 +1,589 @@
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.XR.CompositionLayers.Extensions;
|
||||
using Unity.XR.CompositionLayers.Layers;
|
||||
using Unity.XR.CompositionLayers.Services;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
#if UNITY_VIDEO
|
||||
using UnityEngine.Video;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.CompositionLayers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base implementation for the <see cref="OpenXRLayerProvider.ILayerHandler"/> interface.
|
||||
/// You can implement the required methods of this abstract class to create a concrete layer handler.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <see cref="OpenXRLayerProvider.ILayerHandler"/> methods that this class implements handle adding
|
||||
/// and removing composition layers from native arrays, swap chain creation dispatching and other tasks
|
||||
/// required by the Unity side of the API.
|
||||
///
|
||||
/// The abstract methods that you must implement handle the custom aspects of your layer. These methods include:
|
||||
///
|
||||
/// * <see cref="CreateSwapchain(CompositionLayerManager.LayerInfo, out SwapchainCreateInfo)"/>
|
||||
/// * <see cref="CreateNativeLayer(CompositionLayerManager.LayerInfo, OpenXRCustomLayerHandler{T}.SwapchainCreatedOutput, out T)"/>
|
||||
/// * <see cref="ModifyNativeLayer(CompositionLayerManager.LayerInfo, ref T)"/>
|
||||
///
|
||||
/// You are not required to implement a custom layer handler based on this abstract class, but doing so should be
|
||||
/// easier than implementing the <see cref="OpenXRLayerProvider.ILayerHandler"/> interface in its entirety.
|
||||
///
|
||||
/// You must register your concrete layer handler object with
|
||||
/// <see cref="OpenXRLayerProvider.RegisterLayerHandler(Type, OpenXRLayerProvider.ILayerHandler)"/>.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">The native OpenXR structure of the composition layer to handle.</typeparam>
|
||||
public abstract class OpenXRCustomLayerHandler<T> : OpenXRLayerProvider.ILayerHandler, IDisposable where T : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Container for swapchain related information that may be needed during the creation of the native OpenXR composition layer struct.
|
||||
/// </summary>
|
||||
protected struct SwapchainCreateInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Native structure for the swapchain creation info.
|
||||
/// </summary>
|
||||
public XrSwapchainCreateInfo nativeStruct;
|
||||
|
||||
/// <summary>
|
||||
/// Tells if swapchain is using an external surface.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool isExternalSurface;
|
||||
|
||||
/// <summary>
|
||||
/// Tells if swapchain should be stereo.
|
||||
/// </summary>
|
||||
public bool isStereo;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes and returns an instance of SwapchainCreateInfo with the provided parameters.
|
||||
/// </summary>
|
||||
/// <param name="xrSwapchainCreateInfo">Native structure for the swapchain creation info.</param>
|
||||
/// <param name="isExternalSurface">Tells if swapchain is using an external surface.</param>
|
||||
/// <param name="isStereo">Tells if swapchain should be stereo.</param>
|
||||
public SwapchainCreateInfo(XrSwapchainCreateInfo xrSwapchainCreateInfo, bool isExternalSurface = false, bool isStereo = false)
|
||||
{
|
||||
this.nativeStruct = xrSwapchainCreateInfo;
|
||||
this.isExternalSurface = isExternalSurface;
|
||||
this.isStereo = isStereo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion with just a native XrSwapchainCreateInfo struct.
|
||||
/// </summary>
|
||||
/// <param name="createInfo">The native struct to convert.</param>
|
||||
public static implicit operator SwapchainCreateInfo(XrSwapchainCreateInfo createInfo) => new SwapchainCreateInfo(createInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Container for swapchain related information that may be needed during the creation of the native OpenXR composition layer struct.
|
||||
/// </summary>
|
||||
protected struct SwapchainCreatedOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// The handle of the created swapchain.
|
||||
/// Can be used to initialize the swapchain member of a native OpenXR composition layer struct.
|
||||
/// </summary>
|
||||
public ulong handle;
|
||||
|
||||
/// <summary>
|
||||
/// The second handle of the created stereo swapchain.
|
||||
/// Can be used to initialize the swapchain member of a native OpenXR composition layer struct.
|
||||
/// </summary>
|
||||
public ulong secondStereoHandle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Container for grouping render information for each compostion layer.
|
||||
/// </summary>
|
||||
class LayerRenderInfo
|
||||
{
|
||||
public RenderTexture RenderTexture;
|
||||
public Texture Texture;
|
||||
#if UNITY_VIDEO
|
||||
public VideoPlayer videoPlayer;
|
||||
#endif
|
||||
public MeshCollider meshCollider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes and returns an instance of this <c>OpenXRCustomLayerHandler<T></c> while also setting the singleton instance member.
|
||||
/// </summary>
|
||||
protected OpenXRCustomLayerHandler() => Instance = this;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of this specific handler.
|
||||
/// </summary>
|
||||
protected static OpenXRCustomLayerHandler<T> Instance;
|
||||
|
||||
/// <summary>
|
||||
/// Deinitializes this instance of c>OpenXRCustomLayerHandler<T></c>.
|
||||
/// </summary>
|
||||
~OpenXRCustomLayerHandler() => Dispose(false);
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to create the <see cref="XrSwapchainCreateInfo"/> struct that is passed to OpenXR
|
||||
/// to create a swapchain.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To add extensions when constructing the <see cref="XrSwapchainCreateInfo"/> struct, initialize
|
||||
/// the <c>Next</c> pointer with
|
||||
/// <see cref="OpenXRLayerUtility.GetExtensionsChain(CompositionLayerManager.LayerInfo, Unity.XR.CompositionLayers.CompositionLayerExtension.ExtensionTarget)"/>.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// Constructs an XrSwapchainCreateInfo struct with some members using component data.
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// protected override bool CreateSwapchain(CompositionLayerManager.LayerInfo layerInfo, out SwapchainCreateInfo swapchainCreateInfo)
|
||||
/// {
|
||||
/// var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
/// unsafe
|
||||
/// {
|
||||
/// swapchainCreateInfo = new XrSwapchainCreateInfo()
|
||||
/// {
|
||||
/// Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
|
||||
/// Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Swapchain),
|
||||
/// CreateFlags = 0,
|
||||
/// UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
|
||||
/// Format = OpenXRLayerUtility.GetDefaultColorFormat(),
|
||||
/// SampleCount = 1,
|
||||
/// Width = (uint)texturesExtension.LeftTexture.width,
|
||||
/// Height = (uint)texturesExtension.LeftTexture.height,
|
||||
/// FaceCount = 1,
|
||||
/// ArraySize = 1,
|
||||
/// MipCount = (uint)texturesExtension.LeftTexture.mipmapCount,
|
||||
/// };
|
||||
/// }
|
||||
/// return true;
|
||||
/// }
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
|
||||
/// that was just created.</param>
|
||||
/// <param name="swapchainCreateInfo"> An <c>XrSwapchainCreateInfo</c> object created and initialized by the concrete implementation of this method.</returns>
|
||||
/// <returns> A bool indicating success or failure.</returns>
|
||||
|
||||
protected abstract bool CreateSwapchain(CompositionLayerManager.LayerInfo layerInfo, out SwapchainCreateInfo swapchainCreateInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to create the native composition layer struct of type T that is passed to OpenXR.
|
||||
/// A swapchain info struct is provided so your layer handler has access to any needed swapchain information.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To add extensions when constructing the <see cref="XrSwapchainCreateInfo"/> struct, initialize
|
||||
/// the <c>Next</c> pointer with <see cref="OpenXRLayerUtility.GetExtensionsChain(CompositionLayerManager.LayerInfo, Unity.XR.CompositionLayers.CompositionLayerExtension.ExtensionTarget)"/>.
|
||||
///
|
||||
/// If your struct needs any XrSpace relative info you can use <see cref="OpenXRLayerUtility.GetCurrentAppSpace"/>
|
||||
/// to get the current app space.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// Constructs an XrCompositionLayerQuad struct with some members using component data.
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// protected override bool CreateNativeLayer(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerQuad nativeLayer)
|
||||
/// {
|
||||
/// var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
/// var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
///
|
||||
/// unsafe
|
||||
/// {
|
||||
/// var data = layerInfo.Layer.LayerData as QuadLayerData;
|
||||
///
|
||||
/// nativeLayer = new XrCompositionLayerQuad()
|
||||
/// {
|
||||
/// Type = 36,
|
||||
/// Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer),
|
||||
/// LayerFlags = 0x00000002,
|
||||
/// Space = OpenXRLayerUtility.GetCurrentAppSpace(),
|
||||
/// EyeVisibility = 0,
|
||||
/// SubImage = new SwapchainSubImage()
|
||||
/// {
|
||||
/// Swapchain = swapchainOutput.handle,
|
||||
/// ImageRect = new XrRect2Di()
|
||||
/// {
|
||||
/// offset = new XrOffset2Di() { x = 0, y = 0 },
|
||||
/// extent = new XrExtent2Di()
|
||||
/// {
|
||||
/// width = texturesExtension.LeftTexture.width,
|
||||
/// height = texturesExtension.LeftTexture.height
|
||||
/// }
|
||||
/// },
|
||||
/// ImageArrayIndex = 0
|
||||
/// },
|
||||
/// Pose = new XrPosef(transform.position, transform.rotation),
|
||||
/// Size = new XrExtend2Df()
|
||||
/// {
|
||||
/// width = data.GetScaledSize(transform.lossyScale).x,
|
||||
/// height = data.GetScaledSize(transform.lossyScale).y
|
||||
/// }
|
||||
/// };
|
||||
/// }
|
||||
/// return true;
|
||||
/// }
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
|
||||
/// that was just created.</param>
|
||||
/// <param name="swapchainOutput"> Information regarding the swapchain that was created for this layer,
|
||||
/// such as the associated swapchain handle.</param>
|
||||
/// <param name="nativeLayer"> An object of type T that is created and initialized by the concrete implementation of this method.</returns>
|
||||
/// <returns> A bool indicating success or failure.</returns>
|
||||
|
||||
protected abstract bool CreateNativeLayer(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput, out T nativeLayer);
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to modify a native composition layer struct in response to changes on the associated
|
||||
/// <see cref="Unity.XR.CompositionLayers.Layers.LayerData"/> object or any extension components on the
|
||||
/// <see cref="Unity.XR.CompositionLayers.CompositionLayer"/> GameObject.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You must reinitialize the Next pointer with <see cref="OpenXRLayerUtility.GetExtensionsChain(CompositionLayerManager.LayerInfo, Unity.XR.CompositionLayers.CompositionLayerExtension.ExtensionTarget)"/>
|
||||
/// to get any potential updates from extension components.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// Modifies an XrCompositionLayerQuad struct with new transform and extension information.
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// protected override bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerQuad nativeLayer)
|
||||
/// {
|
||||
/// var data = layerInfo.Layer.LayerData as QuadLayerData;
|
||||
/// var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
///
|
||||
/// nativeLayer.Pose = new XrPosef(transform.position, transform.rotation);
|
||||
/// nativeLayer.Size = new XrExtend2Df()
|
||||
/// {
|
||||
/// width = data.GetScaledSize(transform.lossyScale).x,
|
||||
/// height = data.GetScaledSize(transform.lossyScale).y
|
||||
/// };
|
||||
///
|
||||
/// unsafe
|
||||
/// {
|
||||
/// nativeLayer.Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
|
||||
/// }
|
||||
/// return true;
|
||||
/// }
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition
|
||||
/// layer that was modified.</param>
|
||||
/// <param name="nativeLayer"> A reference to the native OpenXR structure of the composition layer that was modified.
|
||||
/// The concrete implementation of this method should update the values of the structure as appropriate.</param>
|
||||
/// <returns> A bool indicating success or failure.</returns>
|
||||
protected abstract bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref T nativeLayer);
|
||||
|
||||
/// <summary>
|
||||
/// Mapping of instance ids and native layer structs to help determine what layers are currently set to be active.
|
||||
/// </summary>
|
||||
protected Dictionary<int, T> m_nativeLayers = new Dictionary<int, T>();
|
||||
|
||||
/// <summary>
|
||||
/// Thread safe queue used to dispatch callbacks that may come from other threads such as the swapchain creation
|
||||
/// on the graphics thread.
|
||||
/// </summary>
|
||||
protected ConcurrentQueue<Action> actionsForMainThread = new ConcurrentQueue<Action>();
|
||||
|
||||
Dictionary<int, LayerRenderInfo> m_renderInfos = new Dictionary<int, LayerRenderInfo>();
|
||||
Dictionary<int, CompositionLayerManager.LayerInfo> m_layerInfos = new Dictionary<int, CompositionLayerManager.LayerInfo>();
|
||||
NativeArray<T> m_ActiveNativeLayers;
|
||||
NativeArray<int> m_ActiveNativeLayerOrders;
|
||||
int m_ActiveNativeLayerCount;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the <see cref="OpenXRLayerProvider.ILayerHandler"/> method that is called by the
|
||||
/// <see cref="OpenXRLayerProvider"/> during the Unity update loop.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This implementation carries out two tasks. It dequeues actions for the main thread like dispatch when
|
||||
/// the swapchain has been
|
||||
/// created and it adds all the active layers to the <c>endFrameInfo</c> struct in the native UnityOpenXR lib.
|
||||
/// </remarks>
|
||||
public virtual void OnUpdate()
|
||||
{
|
||||
while (actionsForMainThread.Count > 0)
|
||||
{
|
||||
if (actionsForMainThread.TryDequeue(out Action action))
|
||||
action();
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
if (m_ActiveNativeLayerCount > 0)
|
||||
OpenXRLayerUtility.AddActiveLayersToEndFrame(m_ActiveNativeLayers.GetUnsafePtr(), m_ActiveNativeLayerOrders.GetUnsafePtr(), m_ActiveNativeLayerCount, UnsafeUtility.SizeOf<T>());
|
||||
}
|
||||
|
||||
m_ActiveNativeLayerCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the <see cref="OpenXRLayerProvider.ILayerHandler"/> method that is called by the
|
||||
/// <see cref="OpenXRLayerProvider"/> when a new layer has been created.
|
||||
/// This implementation triggers the creation of a swapchain before the actual native layer struct is created.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
|
||||
/// being created.</param>
|
||||
public void CreateLayer(CompositionLayerManager.LayerInfo layerInfo)
|
||||
{
|
||||
CreateSwapchainAsync(layerInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the <see cref="OpenXRLayerProvider.ILayerHandler"/> method that is called by the
|
||||
/// <see cref="OpenXRLayerProvider"/> when a layer or attached extension has been modified.
|
||||
/// This implementation asks the subclass for any changes that must be made to the layer via
|
||||
/// <see cref="ModifyNativeLayer(CompositionLayerManager.LayerInfo, ref T)"/>
|
||||
/// by sending a reference to the native layer struct.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
|
||||
/// that was modified.</param>
|
||||
public virtual void ModifyLayer(CompositionLayerManager.LayerInfo layerInfo)
|
||||
{
|
||||
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
|
||||
if (!m_nativeLayers.TryGetValue(layerInfo.Id, out var nativeLayer))
|
||||
{
|
||||
if (texturesExtension != null && texturesExtension.TextureAdded)
|
||||
{
|
||||
texturesExtension.TextureAdded = false;
|
||||
CreateLayer(layerInfo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var success = ModifyNativeLayer(layerInfo, ref nativeLayer);
|
||||
if (success)
|
||||
m_nativeLayers[layerInfo.Id] = nativeLayer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the <see cref="OpenXRLayerProvider.ILayerHandler"/> method that is called by the
|
||||
/// <see cref="OpenXRLayerProvider"/> when a layer is destroyed or disabled.
|
||||
/// </summary>
|
||||
/// <param name="removedLayerId"> The instance id of the CompositionLayer component that was removed.</param>
|
||||
public virtual void RemoveLayer(int removedLayerId)
|
||||
{
|
||||
OpenXRLayerUtility.ReleaseSwapchain(removedLayerId);
|
||||
m_nativeLayers.Remove(removedLayerId);
|
||||
m_layerInfos.Remove(removedLayerId);
|
||||
m_renderInfos.Remove(removedLayerId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the <see cref="OpenXRLayerProvider.ILayerHandler"/> method that is called by the
|
||||
/// <see cref="OpenXRLayerProvider"/> when a layer is considered to be currently active.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
|
||||
/// being set to active.</param>
|
||||
public virtual void SetActiveLayer(CompositionLayerManager.LayerInfo layerInfo)
|
||||
{
|
||||
if (!m_nativeLayers.TryGetValue(layerInfo.Id, out var nativeLayer))
|
||||
return;
|
||||
|
||||
var success = ActiveNativeLayer(layerInfo, ref nativeLayer);
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
m_nativeLayers[layerInfo.Id] = nativeLayer;
|
||||
ResizeNativeArrays();
|
||||
m_ActiveNativeLayers[m_ActiveNativeLayerCount] = m_nativeLayers[layerInfo.Id];
|
||||
m_ActiveNativeLayerOrders[m_ActiveNativeLayerCount] = layerInfo.Layer.Order;
|
||||
++m_ActiveNativeLayerCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements method from <see cref="IDisposable"/> that is called by the <see cref="OpenXRLayerProvider"/>
|
||||
/// when this custom layer handler instance is disposed.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all maps and disposes any created native arrays.
|
||||
/// </summary>
|
||||
/// <param name="disposing">Determines if this method was called from the Dispose() method or the finalizer.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
m_layerInfos.Clear();
|
||||
m_nativeLayers.Clear();
|
||||
m_renderInfos.Clear();
|
||||
}
|
||||
|
||||
if (m_ActiveNativeLayers.IsCreated)
|
||||
m_ActiveNativeLayers.Dispose();
|
||||
if (m_ActiveNativeLayerOrders.IsCreated)
|
||||
m_ActiveNativeLayerOrders.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="CreateSwapchain(CompositionLayerManager.LayerInfo, out SwapchainCreateInfo)"/> to create a
|
||||
/// <see cref="SwapchainCreateInfo"/> struct that is then passed to the
|
||||
/// UnityOpenXR lib to actually create the swapchain on the graphics thread.
|
||||
/// The static <see cref="OnCreatedSwapchainCallback(int, ulong)"/> method is passed as a callback and invoked when
|
||||
/// the swapchain has been created.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
|
||||
/// that was just created.</param>
|
||||
protected virtual void CreateSwapchainAsync(CompositionLayerManager.LayerInfo layerInfo)
|
||||
{
|
||||
m_layerInfos[layerInfo.Id] = layerInfo;
|
||||
var success = CreateSwapchain(layerInfo, out var swapChainInfo);
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
if (swapChainInfo.isStereo)
|
||||
OpenXRLayerUtility.CreateStereoSwapchain(layerInfo.Id, swapChainInfo.nativeStruct, OnCreatedStereoSwapchainCallback);
|
||||
else
|
||||
OpenXRLayerUtility.CreateSwapchain(layerInfo.Id, swapChainInfo.nativeStruct, swapChainInfo.isExternalSurface, OnCreatedSwapchainCallback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is dispatched to the main thread inside <see cref="OnCreatedSwapchainCallback(int, ulong)"/>
|
||||
/// and asks this subclass to create the native layer struct by invoking
|
||||
/// <see cref="CreateNativeLayer(CompositionLayerManager.LayerInfo, SwapchainCreatedOutput, out T)"/>.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
|
||||
/// that was just created.</param>
|
||||
/// <param name="swapchainOutput"> Information regarding the swapchain that was created for this layer, such as
|
||||
/// the associated swapchain handle.</param>
|
||||
protected virtual void OnCreatedSwapchain(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput)
|
||||
{
|
||||
var success = CreateNativeLayer(layerInfo, swapchainOutput, out var nativeLayer);
|
||||
if (success)
|
||||
m_nativeLayers[layerInfo.Id] = nativeLayer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the native arrays are of the same size as the m_nativeLayers map.
|
||||
/// </summary>
|
||||
protected virtual void ResizeNativeArrays()
|
||||
{
|
||||
if (!m_ActiveNativeLayers.IsCreated && !m_ActiveNativeLayerOrders.IsCreated)
|
||||
{
|
||||
m_ActiveNativeLayers = new NativeArray<T>(m_nativeLayers.Count, Allocator.Persistent);
|
||||
m_ActiveNativeLayerOrders = new NativeArray<int>(m_nativeLayers.Count, Allocator.Persistent);
|
||||
return;
|
||||
}
|
||||
|
||||
Assertions.Assert.AreEqual(m_ActiveNativeLayers.Length, m_ActiveNativeLayerOrders.Length);
|
||||
|
||||
if (m_ActiveNativeLayers.Length < m_nativeLayers.Count)
|
||||
{
|
||||
var newLayerArray = new NativeArray<T>(m_nativeLayers.Count, Allocator.Persistent);
|
||||
NativeArray<T>.Copy(m_ActiveNativeLayers, newLayerArray, m_ActiveNativeLayers.Length);
|
||||
m_ActiveNativeLayers.Dispose();
|
||||
m_ActiveNativeLayers = newLayerArray;
|
||||
|
||||
var newOrderArray = new NativeArray<int>(m_nativeLayers.Count, Allocator.Persistent);
|
||||
NativeArray<int>.Copy(m_ActiveNativeLayerOrders, newOrderArray, m_ActiveNativeLayerOrders.Length);
|
||||
m_ActiveNativeLayerOrders.Dispose();
|
||||
m_ActiveNativeLayerOrders = newOrderArray;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to modify a native composition layer struct in response to when it is active.
|
||||
/// An active compositon layer will invoke this every frame.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition
|
||||
/// layer that is active.</param>
|
||||
/// <param name="nativeLayer"> A reference to the native OpenXR structure of the composition layer that is active.</param>
|
||||
/// <returns>Bool indicating success or failure. A failure case will result in the native composition layer struct not being added into the final XrFrameEndInfo struct.</returns>
|
||||
protected virtual bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref T nativeLayer)
|
||||
{
|
||||
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.LeftTexture == null || texturesExtension.sourceTexture == TexturesExtension.SourceTextureEnum.AndroidSurface)
|
||||
return true;
|
||||
|
||||
if (m_renderInfos.TryGetValue(layerInfo.Id, out var container))
|
||||
{
|
||||
bool isNewTexture = container.Texture != texturesExtension.LeftTexture;
|
||||
|
||||
if (isNewTexture)
|
||||
{
|
||||
// If we have a new texture with different dimensions then we need to release the current swapchain and create another.
|
||||
// This is an async procedure that also creates a new native layer object.
|
||||
if (container.Texture.width != texturesExtension.LeftTexture.width || container.Texture.height != texturesExtension.LeftTexture.height)
|
||||
{
|
||||
RemoveLayer(layerInfo.Id);
|
||||
CreateSwapchainAsync(layerInfo);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
container.Texture = texturesExtension.LeftTexture;
|
||||
#if UNITY_VIDEO
|
||||
container.videoPlayer = layerInfo.Layer.GetComponent<VideoPlayer>();
|
||||
#endif
|
||||
container.meshCollider = layerInfo.Layer.GetComponent<MeshCollider>();
|
||||
}
|
||||
|
||||
bool isVideo = false;
|
||||
#if UNITY_VIDEO
|
||||
isVideo = container.videoPlayer != null && container.videoPlayer.enabled;
|
||||
#endif
|
||||
bool isUI = container.meshCollider != null && container.meshCollider.enabled;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Layers with a video or ui component in editor may have multiple native render textures associated with the layer id so we must find them.
|
||||
if (isVideo || isUI)
|
||||
OpenXRLayerUtility.FindAndWriteToRenderTexture(layerInfo, container.Texture, out container.RenderTexture);
|
||||
else if (isNewTexture)
|
||||
OpenXRLayerUtility.WriteToRenderTexture(container.Texture, container.RenderTexture);
|
||||
#else
|
||||
// We only need to write continuously to the native render texture if our texture is changing.
|
||||
if (isVideo || isUI || isNewTexture)
|
||||
OpenXRLayerUtility.WriteToRenderTexture(container.Texture, container.RenderTexture);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
bool isRenderTextureWritten = OpenXRLayerUtility.FindAndWriteToRenderTexture(layerInfo, texturesExtension.LeftTexture, out RenderTexture renderTexture);
|
||||
if (isRenderTextureWritten)
|
||||
{
|
||||
var layerRenderInfo = new LayerRenderInfo()
|
||||
{ Texture = texturesExtension.LeftTexture, RenderTexture = renderTexture,
|
||||
#if UNITY_VIDEO
|
||||
videoPlayer = layerInfo.Layer.GetComponent<VideoPlayer>(),
|
||||
#endif
|
||||
meshCollider = layerInfo.Layer.GetComponent<MeshCollider>() };
|
||||
m_renderInfos.Add(layerInfo.Id, layerRenderInfo);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[AOT.MonoPInvokeCallback(typeof(OpenXRLayerUtility.SwapchainCallbackDelegate))]
|
||||
static void OnCreatedSwapchainCallback(int layerId, ulong swapchainHandle)
|
||||
{
|
||||
if (Instance == null)
|
||||
return;
|
||||
|
||||
Instance.actionsForMainThread.Enqueue(() => { Instance.OnCreatedSwapchain(Instance.m_layerInfos[layerId], new SwapchainCreatedOutput { handle = swapchainHandle });});
|
||||
}
|
||||
|
||||
[AOT.MonoPInvokeCallback(typeof(OpenXRLayerUtility.StereoSwapchainCallbackDelegate))]
|
||||
static void OnCreatedStereoSwapchainCallback(int layerId, ulong swapchainHandleLeft, ulong swapchainHandleRight)
|
||||
{
|
||||
if (Instance == null)
|
||||
return;
|
||||
|
||||
Instance.actionsForMainThread.Enqueue(() => { Instance.OnCreatedSwapchain(Instance.m_layerInfos[layerId], new SwapchainCreatedOutput { handle = swapchainHandleLeft, secondStereoHandle = swapchainHandleRight}); });
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6aba3e678d47ef44585d1e55c9d4d821
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,316 @@
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using Unity.XR.CompositionLayers;
|
||||
using Unity.XR.CompositionLayers.Extensions;
|
||||
using Unity.XR.CompositionLayers.Layers;
|
||||
using Unity.XR.CompositionLayers.Services;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.CompositionLayers
|
||||
{
|
||||
internal class OpenXRCylinderLayer : OpenXRCustomLayerHandler<XrCompositionLayerCylinderKHR>
|
||||
{
|
||||
public static bool ExtensionEnabled = OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_cylinder");
|
||||
|
||||
float savedDelta;
|
||||
bool layerDataChanged = false;
|
||||
|
||||
struct CylinderLayerSize
|
||||
{
|
||||
public float radius;
|
||||
public float centralAngle;
|
||||
public float aspectRatio;
|
||||
|
||||
public static implicit operator CylinderLayerSize(Vector3 v) => new CylinderLayerSize
|
||||
{
|
||||
radius = v.x,
|
||||
centralAngle = v.y,
|
||||
aspectRatio = v.z
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool CreateSwapchain(CompositionLayerManager.LayerInfo layer, out SwapchainCreateInfo swapchainCreateInfo)
|
||||
{
|
||||
if (layer.Layer == null)
|
||||
{
|
||||
swapchainCreateInfo = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
var texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false)
|
||||
{
|
||||
swapchainCreateInfo = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (texturesExtension.sourceTexture)
|
||||
{
|
||||
case TexturesExtension.SourceTextureEnum.LocalTexture:
|
||||
{
|
||||
if (texturesExtension.LeftTexture == null)
|
||||
goto default;
|
||||
|
||||
var xrCreateInfo = new XrSwapchainCreateInfo()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
|
||||
CreateFlags = 0,
|
||||
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
|
||||
Format = OpenXRLayerUtility.GetDefaultColorFormat(),
|
||||
SampleCount = 1,
|
||||
Width = (uint)texturesExtension.LeftTexture.width,
|
||||
Height = (uint)texturesExtension.LeftTexture.height,
|
||||
FaceCount = 1,
|
||||
ArraySize = 1,
|
||||
MipCount = (uint)texturesExtension.LeftTexture.mipmapCount,
|
||||
};
|
||||
|
||||
swapchainCreateInfo = new SwapchainCreateInfo(xrCreateInfo, isExternalSurface: false);
|
||||
return true;
|
||||
}
|
||||
|
||||
case TexturesExtension.SourceTextureEnum.AndroidSurface:
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
var xrCreateInfo = new XrSwapchainCreateInfo()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
|
||||
CreateFlags = 0,
|
||||
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
|
||||
Format = 0,
|
||||
SampleCount = 0,
|
||||
Width = (uint)texturesExtension.Resolution.x,
|
||||
Height = (uint)texturesExtension.Resolution.y,
|
||||
FaceCount = 0,
|
||||
ArraySize = 0,
|
||||
MipCount = 0,
|
||||
};
|
||||
swapchainCreateInfo = new SwapchainCreateInfo(xrCreateInfo, isExternalSurface: true);
|
||||
return true;
|
||||
#else
|
||||
goto default;
|
||||
#endif
|
||||
}
|
||||
|
||||
default:
|
||||
swapchainCreateInfo = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CreateNativeLayer(CompositionLayerManager.LayerInfo layer, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerCylinderKHR nativeLayer)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var data = layer.Layer.LayerData as CylinderLayerData;
|
||||
var transform = layer.Layer.GetComponent<Transform>();
|
||||
var texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
|
||||
int subImageWidth = 0;
|
||||
int subImageHeight = 0;
|
||||
|
||||
switch (texturesExtension.sourceTexture)
|
||||
{
|
||||
case TexturesExtension.SourceTextureEnum.LocalTexture:
|
||||
{
|
||||
if (texturesExtension.LeftTexture != null)
|
||||
{
|
||||
subImageWidth = texturesExtension.LeftTexture.width;
|
||||
subImageHeight = texturesExtension.LeftTexture.height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TexturesExtension.SourceTextureEnum.AndroidSurface:
|
||||
{
|
||||
subImageWidth = (int)texturesExtension.Resolution.x;
|
||||
subImageHeight = (int)texturesExtension.Resolution.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CylinderLayerSize scaledSize = data.GetScaledSize(transform.lossyScale);
|
||||
if (texturesExtension.CropToAspect)
|
||||
{
|
||||
scaledSize = FixAspectRatio(data, scaledSize, subImageWidth, subImageHeight);
|
||||
}
|
||||
|
||||
nativeLayer = new XrCompositionLayerCylinderKHR()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Layer),
|
||||
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
|
||||
Space = OpenXRLayerUtility.GetCurrentAppSpace(),
|
||||
EyeVisibility = 0,
|
||||
SubImage = new XrSwapchainSubImage()
|
||||
{
|
||||
Swapchain = swapchainOutput.handle,
|
||||
ImageRect = new XrRect2Di()
|
||||
{
|
||||
Offset = new XrOffset2Di() { X = 0, Y = 0 },
|
||||
Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = subImageWidth,
|
||||
Height = subImageHeight
|
||||
}
|
||||
},
|
||||
ImageArrayIndex = 0
|
||||
},
|
||||
Pose = new XrPosef(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation),
|
||||
Radius = data.ApplyTransformScale ? scaledSize.radius : data.Radius,
|
||||
CentralAngle = data.ApplyTransformScale ? scaledSize.centralAngle : data.CentralAngle,
|
||||
AspectRatio = data.ApplyTransformScale ? scaledSize.aspectRatio : data.AspectRatio,
|
||||
};
|
||||
layerDataChanged = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerCylinderKHR nativeLayer)
|
||||
{
|
||||
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false)
|
||||
return false;
|
||||
|
||||
var data = layerInfo.Layer.LayerData as CylinderLayerData;
|
||||
GetSubImageDimensions(out int subImageWidth, out int subImageHeight, texturesExtension);
|
||||
nativeLayer.SubImage.ImageRect.Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = subImageWidth,
|
||||
Height = subImageHeight
|
||||
};
|
||||
|
||||
var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
CylinderLayerSize scaledSize = data.GetScaledSize(transform.lossyScale);
|
||||
if (texturesExtension.CropToAspect)
|
||||
{
|
||||
scaledSize = FixAspectRatio(data, scaledSize, subImageWidth, subImageHeight);
|
||||
}
|
||||
nativeLayer.Radius = data.ApplyTransformScale ? scaledSize.radius : data.Radius;
|
||||
nativeLayer.CentralAngle = data.ApplyTransformScale ? scaledSize.centralAngle : data.CentralAngle;
|
||||
nativeLayer.AspectRatio = data.ApplyTransformScale ? scaledSize.aspectRatio : data.AspectRatio;
|
||||
|
||||
unsafe
|
||||
{
|
||||
nativeLayer.Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
|
||||
}
|
||||
layerDataChanged = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerCylinderKHR nativeLayer)
|
||||
{
|
||||
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false)
|
||||
return false;
|
||||
|
||||
var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
|
||||
// Special treatment for cylinder type based on destination rects.
|
||||
if (texturesExtension != null && texturesExtension.CustomRects)
|
||||
{
|
||||
var cylinderLayer = layerInfo.Layer.LayerData as CylinderLayerData;
|
||||
float rotationDelta = (texturesExtension.LeftEyeDestinationRect.x + (0.5f * texturesExtension.LeftEyeDestinationRect.width) - 0.5f) * cylinderLayer.CentralAngle / (float)System.Math.PI * 180.0f;
|
||||
|
||||
if (rotationDelta != savedDelta)
|
||||
{
|
||||
Quaternion savedDeltaQuaternion = Quaternion.AngleAxis(savedDelta, Vector3.up);
|
||||
Quaternion deltaQuaternion = Quaternion.AngleAxis(rotationDelta, Vector3.up);
|
||||
Quaternion difference = deltaQuaternion * Quaternion.Inverse(savedDeltaQuaternion);
|
||||
|
||||
savedDelta = rotationDelta;
|
||||
transform.rotation *= difference;
|
||||
}
|
||||
}
|
||||
|
||||
nativeLayer.Pose = new XrPosef(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
|
||||
nativeLayer.Space = OpenXRLayerUtility.GetCurrentAppSpace();
|
||||
|
||||
if (texturesExtension.CustomRects && layerDataChanged)
|
||||
{
|
||||
GetSubImageDimensions(out int subImageWidth, out int subImageHeight, texturesExtension);
|
||||
|
||||
nativeLayer.SubImage.ImageRect = new XrRect2Di()
|
||||
{
|
||||
Offset = new XrOffset2Di()
|
||||
{
|
||||
X = (int)(subImageWidth * texturesExtension.LeftEyeSourceRect.x),
|
||||
Y = (int)(subImageHeight * texturesExtension.LeftEyeSourceRect.y)
|
||||
},
|
||||
|
||||
Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = (int)(subImageWidth * texturesExtension.LeftEyeSourceRect.width),
|
||||
Height = (int)(subImageHeight * texturesExtension.LeftEyeSourceRect.height)
|
||||
}
|
||||
};
|
||||
|
||||
var currentPosition = OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position;
|
||||
float cylinderHeight = nativeLayer.Radius * nativeLayer.CentralAngle / nativeLayer.AspectRatio;
|
||||
float transformedY = currentPosition.y + (((texturesExtension.LeftEyeDestinationRect.y + (0.5f * texturesExtension.LeftEyeDestinationRect.height) - 0.5f)) * (-1.0f * cylinderHeight));
|
||||
nativeLayer.Pose = new XrPosef(new Vector3(currentPosition.x, transformedY, currentPosition.z), OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
|
||||
|
||||
nativeLayer.CentralAngle = nativeLayer.CentralAngle * texturesExtension.LeftEyeDestinationRect.width;
|
||||
nativeLayer.AspectRatio = nativeLayer.AspectRatio * texturesExtension.LeftEyeDestinationRect.width / texturesExtension.LeftEyeDestinationRect.height;
|
||||
layerDataChanged = false;
|
||||
}
|
||||
|
||||
return base.ActiveNativeLayer(layerInfo, ref nativeLayer);
|
||||
}
|
||||
|
||||
static CylinderLayerSize FixAspectRatio(CylinderLayerData data, CylinderLayerSize scaledSize, int texWidth, int texHeight)
|
||||
{
|
||||
// because we're cropping and trying to maintain the same other parameters, we don't
|
||||
// need to consider data.MaintainAspectRatio here. That's mostly an editor concern, anyway.
|
||||
float texRatio = (float)texWidth / (float)texHeight;
|
||||
if (scaledSize.aspectRatio > texRatio)
|
||||
{
|
||||
// too wide
|
||||
float width = scaledSize.radius * scaledSize.centralAngle;
|
||||
float height = width / scaledSize.aspectRatio;
|
||||
scaledSize.centralAngle = height * texRatio / scaledSize.radius;
|
||||
scaledSize.aspectRatio = texRatio;
|
||||
}
|
||||
else if (scaledSize.aspectRatio < texRatio)
|
||||
{
|
||||
// too narrow
|
||||
scaledSize.aspectRatio = texRatio;
|
||||
}
|
||||
|
||||
return scaledSize;
|
||||
}
|
||||
|
||||
static void GetSubImageDimensions(out int width, out int height, TexturesExtension texturesExtension)
|
||||
{
|
||||
width = 0;
|
||||
height = 0;
|
||||
|
||||
switch (texturesExtension.sourceTexture)
|
||||
{
|
||||
case TexturesExtension.SourceTextureEnum.LocalTexture:
|
||||
{
|
||||
if (texturesExtension.LeftTexture != null)
|
||||
{
|
||||
width = texturesExtension.LeftTexture.width;
|
||||
height = texturesExtension.LeftTexture.height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TexturesExtension.SourceTextureEnum.AndroidSurface:
|
||||
{
|
||||
width = (int)texturesExtension.Resolution.x;
|
||||
height = (int)texturesExtension.Resolution.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f0fe043a738427795fec6ef768571ed
|
||||
timeCreated: 1681172287
|
||||
@@ -0,0 +1,41 @@
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
|
||||
using Unity.XR.CompositionLayers;
|
||||
using Unity.XR.CompositionLayers.Layers;
|
||||
using Unity.XR.CompositionLayers.Services;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.CompositionLayers
|
||||
{
|
||||
internal class OpenXRDefaultLayer : OpenXRLayerProvider.ILayerHandler
|
||||
{
|
||||
unsafe void SetDefaultLayerAttributes(CompositionLayerManager.LayerInfo layerInfo)
|
||||
{
|
||||
var extensions = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
|
||||
OpenXRLayerUtility.SetDefaultSceneLayerExtensions(extensions);
|
||||
|
||||
var flags = layerInfo.Layer.LayerData.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha;
|
||||
OpenXRLayerUtility.SetDefaultLayerFlags(flags);
|
||||
}
|
||||
|
||||
public void CreateLayer(CompositionLayerManager.LayerInfo layerInfo) => SetDefaultLayerAttributes(layerInfo);
|
||||
|
||||
public void ModifyLayer(CompositionLayerManager.LayerInfo layerInfo) => SetDefaultLayerAttributes(layerInfo);
|
||||
|
||||
public void OnUpdate()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public void RemoveLayer(int id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public void SetActiveLayer(CompositionLayerManager.LayerInfo layerInfo)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e69d044c52e6d64cb5468a07a96b67f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,240 @@
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using System;
|
||||
using Unity.XR.CompositionLayers;
|
||||
using Unity.XR.CompositionLayers.Extensions;
|
||||
using Unity.XR.CompositionLayers.Layers;
|
||||
using Unity.XR.CompositionLayers.Services;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.CompositionLayers
|
||||
{
|
||||
|
||||
//OpenXR Composition Layer - Equirect Layer support : only the interior of the mesh surface will be visible
|
||||
internal class OpenXREquirectLayer : OpenXRCustomLayerHandler<XrCompositionLayerEquirectKHR>
|
||||
{
|
||||
public static bool ExtensionEnabled = OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_equirect");
|
||||
|
||||
protected override unsafe bool CreateSwapchain(CompositionLayerManager.LayerInfo layerInfo, out SwapchainCreateInfo swapchainCreateInfo)
|
||||
{
|
||||
TexturesExtension texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
|
||||
{
|
||||
swapchainCreateInfo = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
swapchainCreateInfo = new XrSwapchainCreateInfo()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Swapchain),
|
||||
CreateFlags = 0,
|
||||
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
|
||||
Format = OpenXRLayerUtility.GetDefaultColorFormat(),
|
||||
SampleCount = 1,
|
||||
Width = (uint)(texturesExtension.LeftTexture.width),
|
||||
Height = (uint)(texturesExtension.LeftTexture.height),
|
||||
FaceCount = 1,
|
||||
ArraySize = 1,
|
||||
MipCount = (uint)texturesExtension.LeftTexture.mipmapCount,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override unsafe bool CreateNativeLayer(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerEquirectKHR nativeLayer)
|
||||
{
|
||||
TexturesExtension texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
|
||||
{
|
||||
nativeLayer = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
var data = layerInfo.Layer.LayerData as EquirectMeshLayerData;
|
||||
Vector2 scaleCalculated = CalculateScale(data.CentralHorizontalAngle, data.UpperVerticalAngle, data.LowerVerticalAngle);
|
||||
Vector2 biasCalculated = CalculateBias(scaleCalculated, data.UpperVerticalAngle);
|
||||
|
||||
nativeLayer = new XrCompositionLayerEquirectKHR()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer),
|
||||
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
|
||||
Space = OpenXRLayerUtility.GetCurrentAppSpace(),
|
||||
EyeVisibility = 0,
|
||||
SubImage = new XrSwapchainSubImage()
|
||||
{
|
||||
Swapchain = swapchainOutput.handle,
|
||||
ImageRect = new XrRect2Di()
|
||||
{
|
||||
Offset = new XrOffset2Di() { X = 0, Y = 0 },
|
||||
Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = texturesExtension.LeftTexture.width,
|
||||
Height = texturesExtension.LeftTexture.height
|
||||
}
|
||||
},
|
||||
ImageArrayIndex = 0
|
||||
},
|
||||
Pose = new XrPosef(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation),
|
||||
Radius = data.Radius,
|
||||
Scale = new XrVector2f(scaleCalculated),
|
||||
Bias = new XrVector2f(biasCalculated),
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override unsafe bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerEquirectKHR nativeLayer)
|
||||
{
|
||||
TexturesExtension texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
|
||||
return false;
|
||||
|
||||
var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
var data = layerInfo.Layer.LayerData as EquirectMeshLayerData;
|
||||
Vector2 scaleCalculated = CalculateScale(data.CentralHorizontalAngle, data.UpperVerticalAngle, data.LowerVerticalAngle);
|
||||
Vector2 biasCalculated = CalculateBias(scaleCalculated, data.UpperVerticalAngle);
|
||||
|
||||
nativeLayer.SubImage.ImageRect.Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = texturesExtension.LeftTexture.width,
|
||||
Height = texturesExtension.LeftTexture.height
|
||||
};
|
||||
nativeLayer.Pose = new XrPosef(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
|
||||
nativeLayer.Radius = data.Radius;
|
||||
nativeLayer.Scale = new XrVector2f(scaleCalculated);
|
||||
nativeLayer.Bias = new XrVector2f(biasCalculated);
|
||||
nativeLayer.Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerEquirectKHR nativeLayer)
|
||||
{
|
||||
nativeLayer.Space = OpenXRLayerUtility.GetCurrentAppSpace();
|
||||
return base.ActiveNativeLayer(layerInfo, ref nativeLayer);
|
||||
}
|
||||
|
||||
Vector2 CalculateScale(float centralHorizontalAngle, float upperVerticalAngle, float lowerVerticalAngle)
|
||||
{
|
||||
return new Vector2((2.0f * (float)Math.PI) / centralHorizontalAngle, (float)Math.PI / (upperVerticalAngle - lowerVerticalAngle));
|
||||
}
|
||||
|
||||
Vector2 CalculateBias(Vector2 scaleCalculated, float upperVerticalAngle)
|
||||
{
|
||||
return new Vector2((1.0f - scaleCalculated.x) * 0.5f, (upperVerticalAngle / (float)Math.PI - 0.5f) * scaleCalculated.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class OpenXREquirect2Layer : OpenXRCustomLayerHandler<XrCompositionLayerEquirect2KHR>
|
||||
{
|
||||
|
||||
public static bool ExtensionEnabled = OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_equirect2");
|
||||
|
||||
protected override unsafe bool CreateSwapchain(CompositionLayerManager.LayerInfo layerInfo, out SwapchainCreateInfo swapchainCreateInfo)
|
||||
{
|
||||
TexturesExtension texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
|
||||
{
|
||||
swapchainCreateInfo = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
swapchainCreateInfo = new XrSwapchainCreateInfo()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Swapchain),
|
||||
CreateFlags = 0,
|
||||
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
|
||||
Format = OpenXRLayerUtility.GetDefaultColorFormat(),
|
||||
SampleCount = 1,
|
||||
Width = (uint)(texturesExtension.LeftTexture.width),
|
||||
Height = (uint)(texturesExtension.LeftTexture.height),
|
||||
FaceCount = 1,
|
||||
ArraySize = 1,
|
||||
MipCount = (uint)texturesExtension.LeftTexture.mipmapCount,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override unsafe bool CreateNativeLayer(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerEquirect2KHR nativeLayer)
|
||||
{
|
||||
TexturesExtension texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
|
||||
{
|
||||
nativeLayer = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
var data = layerInfo.Layer.LayerData as EquirectMeshLayerData;
|
||||
Vector2 scaleCalculated = CalculateScale(data.CentralHorizontalAngle, data.UpperVerticalAngle, data.LowerVerticalAngle);
|
||||
|
||||
nativeLayer = new XrCompositionLayerEquirect2KHR()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer),
|
||||
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
|
||||
Space = OpenXRLayerUtility.GetCurrentAppSpace(),
|
||||
EyeVisibility = 0,
|
||||
SubImage = new XrSwapchainSubImage()
|
||||
{
|
||||
Swapchain = swapchainOutput.handle,
|
||||
ImageRect = new XrRect2Di()
|
||||
{
|
||||
Offset = new XrOffset2Di() { X = 0, Y = 0 },
|
||||
Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = texturesExtension.LeftTexture.width,
|
||||
Height = texturesExtension.LeftTexture.height
|
||||
}
|
||||
},
|
||||
ImageArrayIndex = 0
|
||||
},
|
||||
Pose = new XrPosef(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation),
|
||||
Radius = data.Radius,
|
||||
CentralHorizontalAngle = data.CentralHorizontalAngle,
|
||||
UpperVerticalAngle = data.UpperVerticalAngle,
|
||||
LowerVerticalAngle = -data.LowerVerticalAngle
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override unsafe bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerEquirect2KHR nativeLayer)
|
||||
{
|
||||
TexturesExtension texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
|
||||
return false;
|
||||
|
||||
var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
var data = layerInfo.Layer.LayerData as EquirectMeshLayerData;
|
||||
Vector2 scaleCalculated = CalculateScale(data.CentralHorizontalAngle, data.UpperVerticalAngle, data.LowerVerticalAngle);
|
||||
|
||||
nativeLayer.SubImage.ImageRect.Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = texturesExtension.LeftTexture.width,
|
||||
Height = texturesExtension.LeftTexture.height
|
||||
};
|
||||
nativeLayer.Pose = new XrPosef(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
|
||||
nativeLayer.Radius = data.Radius;
|
||||
nativeLayer.CentralHorizontalAngle = data.CentralHorizontalAngle;
|
||||
nativeLayer.UpperVerticalAngle = data.UpperVerticalAngle;
|
||||
nativeLayer.LowerVerticalAngle = -data.LowerVerticalAngle;
|
||||
|
||||
nativeLayer.Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerEquirect2KHR nativeLayer)
|
||||
{
|
||||
nativeLayer.Space = OpenXRLayerUtility.GetCurrentAppSpace();
|
||||
return base.ActiveNativeLayer(layerInfo, ref nativeLayer);
|
||||
}
|
||||
|
||||
Vector2 CalculateScale(float centralHorizontalAngle, float upperVerticalAngle, float lowerVerticalAngle)
|
||||
{
|
||||
return new Vector2((2.0f * (float)Math.PI) / centralHorizontalAngle, (float)Math.PI / (upperVerticalAngle - lowerVerticalAngle));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cdbb650170aefbc46964b76b43714332
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,299 @@
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
using Unity.Profiling;
|
||||
using Unity.XR.CompositionLayers.Extensions;
|
||||
using Unity.XR.CompositionLayers.Layers;
|
||||
using Unity.XR.CompositionLayers.Provider;
|
||||
using Unity.XR.CompositionLayers.Services;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.CompositionLayers
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages communication of changes between an application and the UnityOpenXR lib for all
|
||||
/// <see cref="Unity.XR.CompositionLayers.Layers.LayerData"/> objects.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// OpenXR providers or extensions that create custom composition layer types or that override how the built-in
|
||||
/// layer types are handled, must implement the <see cref="ILayerProvider"/> interface and register instances of
|
||||
/// these implementations with the <c>OpenXRLayerProvider</c> via <see cref="RegisterLayerHandler(Type, OpenXRLayerProvider.ILayerHandler)"/>.
|
||||
/// </remarks>
|
||||
public class OpenXRLayerProvider : ILayerProvider, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface used by the <see cref="OpenXRLayerProvider"/> to communicate layer data changes to
|
||||
/// registered layer handlers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <c>ILayerHandler</c> instances must register themselves via
|
||||
/// <see cref="OpenXRLayerProvider.RegisterLayerHandler(Type, OpenXRLayerProvider.ILayerHandler)"/>
|
||||
/// to specify the <see cref="LayerData"/> type to handle.
|
||||
/// If more than one object registers itself as a handler for a specific <see cref="LayerData"/>
|
||||
/// type, the last registered handler is used.
|
||||
///
|
||||
/// The <see cref="OpenXRCustomLayerHandler{T}"/> class provides a partial, base implementation of this interface that you can
|
||||
/// use to create custom layer handlers.
|
||||
/// </remarks>
|
||||
public interface ILayerHandler
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Called by the <see cref="OpenXRLayerProvider"/> during the Unity Update loop.
|
||||
/// All implementations must call <see cref="OpenXRLayerUtility.AddActiveLayersToEndFrame(void*,void*,int,int)"/> every frame
|
||||
/// to add their native layer structs to the <c>endFrameInfo</c> struct inside the UnityOpenXR lib.
|
||||
/// </summary>
|
||||
public void OnUpdate();
|
||||
|
||||
/// <summary>
|
||||
/// Called by the <see cref="OpenXRLayerProvider"/> when a new <see cref="LayerData"/>
|
||||
/// object of the type registered to this <c>ILayerHandler</c> instance has been created.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
|
||||
/// that was just created.</param>
|
||||
public void CreateLayer(CompositionLayerManager.LayerInfo layerInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Called by the <see cref="OpenXRLayerProvider"/> when a <see cref="LayerData"/> object
|
||||
/// of the type registered to this <c>ILayerHandler</c> instance has been destroyed or disabled.
|
||||
/// </summary>
|
||||
/// <param name="removedLayerId"> The instance id of the CompositionLayer component that was removed.</param>
|
||||
public void RemoveLayer(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Called by the <see cref="OpenXRLayerProvider"/> when a <see cref="LayerData"/> object
|
||||
/// or any attached extension components have had a member modified.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
|
||||
/// that was modified.</param>
|
||||
public void ModifyLayer(CompositionLayerManager.LayerInfo layerInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Called every frame by the <see cref="OpenXRLayerProvider"/> for all currently active <see cref="LayerData"/> objects
|
||||
/// of the type registered to this <c>ILayerHandler</c> instance.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
|
||||
/// being set to active.</param>
|
||||
public void SetActiveLayer(CompositionLayerManager.LayerInfo layerInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes and returns an instance of <c>OpenXRLayerProvider</c>.
|
||||
/// Initializes and registers all the default, built-in layer handlers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <c>OpenXRLayerProvider</c> is created and disposed by the <see cref="Management.XRLoader"/>.
|
||||
/// You do not need to create an instance of <c>OpenXRLayerProvider</c> yourself. Layer handlers
|
||||
/// should only use the static methods and properties of this class
|
||||
/// </remarks>
|
||||
public OpenXRLayerProvider() => InitializeAndRegisterBuiltInHandlers();
|
||||
|
||||
/// <summary>
|
||||
/// Calls the methods in its invocation list when the <c>OpenXRLayerProvider</c> has started and registered it's built-in layer handlers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You can use this event to wait for the <c>OpenXRLayerProvider</c> to finish registering its built-in layer handlers
|
||||
/// so that you can override them with your own custom layer handlers.
|
||||
/// </remarks>
|
||||
public static event Action Started;
|
||||
|
||||
/// <summary>
|
||||
/// Calls the methods in its invocation list when the <c>OpenXRLayerProvider</c> has stopped and is disposed.
|
||||
/// </summary>
|
||||
public static event Action Stopped;
|
||||
|
||||
/// <summary>
|
||||
/// Reports whether the <c>OpenXRLayerProvider</c> has already been created and started.
|
||||
/// </summary>
|
||||
public static bool isStarted { get; set; }
|
||||
|
||||
static Dictionary<Type, ILayerHandler> LayerHandlers = new Dictionary<Type, ILayerHandler>();
|
||||
static readonly ProfilerMarker s_OpenXRLayerProviderCreate = new ProfilerMarker("OpenXRLayerProvider.Create");
|
||||
static readonly ProfilerMarker s_OpenXRLayerProviderRemove = new ProfilerMarker("OpenXRLayerProvider.Remove");
|
||||
static readonly ProfilerMarker s_OpenXRLayerProviderModify = new ProfilerMarker("OpenXRLayerProvider.Modify");
|
||||
static readonly ProfilerMarker s_OpenXRLayerProviderActive = new ProfilerMarker("OpenXRLayerProvider.Active");
|
||||
static readonly ProfilerMarker s_OpenXRLayerProviderUpdate = new ProfilerMarker("OpenXRLayerProvider.Update");
|
||||
|
||||
/// <summary>
|
||||
/// Registers a concrete <see cref="ILayerHandler"/> object as the handler for all layers of a specific
|
||||
/// <see cref="LayerData"/> subclass.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If more than one object registers itself as a handler for a specific <see cref="LayerData"/>
|
||||
/// type, the last registered handler is used.
|
||||
///
|
||||
/// The <c>OpenXRLayerProvider</c> invokes the registered layer handler's <see cref="ILayerHandler"/> methods
|
||||
/// when any object of the associated <see cref="LayerData"/> type is updated in some way.
|
||||
/// </remarks>
|
||||
/// <param name="layerDataType">The <see cref="LayerData"/> subclass to handle.</param>
|
||||
/// <param name="handler">The concrete <c>ILayerHandler</c> instance> to register.</param>
|
||||
public static void RegisterLayerHandler(Type layerDataType, ILayerHandler handler)
|
||||
{
|
||||
if (handler == null)
|
||||
{
|
||||
LayerHandlers.Remove(layerDataType);
|
||||
return;
|
||||
}
|
||||
|
||||
LayerHandlers[layerDataType] = handler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the layer provider state on first assignment to the <see cref="CompositionLayerManager" />.
|
||||
/// </summary>
|
||||
/// <param name="layers">The list of all currently known <see cref="CompositionLayer"/> instances, regardless of active state.</param>
|
||||
public void SetInitialState(List<CompositionLayerManager.LayerInfo> layers)
|
||||
{
|
||||
UpdateLayers(layers, null, null, null);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called by the <see cref="CompositionLayerManager" /> to tell the instance of <see cref="ILayerProvider" /> about
|
||||
/// the current state of layers it is managing.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="createdLayers">The list of layers that were just created. Any layer in
|
||||
/// this list may be in the <paramref name="activeLayers" /> list if it is activated in the same frame.
|
||||
/// Any layer in this list should not be in <paramref name="modifiedLayers" /> or <paramref name="removedLayers" />.
|
||||
/// This list is ephemeral and cleared after each call.</param>
|
||||
///
|
||||
/// <param name="removedLayers">The list of layers that are no longer being managed. Any layer in
|
||||
/// this list should not be in the <paramref name="createdLayers" />, <paramref name="modifiedLayers" />, or
|
||||
/// <paramref name="activeLayers" /> lists.
|
||||
/// This list is ephemeral and cleared after each call.</param>
|
||||
///
|
||||
/// <param name="modifiedLayers">The list of layers that have been recently modified. Any layer in
|
||||
/// this list may also be in the <paramref name="activeLayers" /> list. Any layer in this list should not
|
||||
/// be in <paramref name="createdLayers" /> or <paramref name="removedLayers" />.
|
||||
/// This list is ephemeral and cleared after each call.</param>
|
||||
///
|
||||
/// <param name="activeLayers">The list of layers currently active within the scene.
|
||||
/// Layers in this list may also be in the <paramref name="createdLayers" /> or <paramref name="modifiedLayers" /> lists
|
||||
/// if they became active in the same frame.</param>
|
||||
public void UpdateLayers(List<CompositionLayerManager.LayerInfo> createdLayers, List<int> removedLayers, List<CompositionLayerManager.LayerInfo> modifiedLayers, List<CompositionLayerManager.LayerInfo> activeLayers)
|
||||
{
|
||||
if (removedLayers != null && removedLayers.Count != 0)
|
||||
{
|
||||
foreach (var handler in LayerHandlers.Values)
|
||||
{
|
||||
foreach (var removed in removedLayers)
|
||||
{
|
||||
s_OpenXRLayerProviderRemove.Begin();
|
||||
handler?.RemoveLayer(removed);
|
||||
s_OpenXRLayerProviderRemove.End();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (createdLayers != null && createdLayers.Count != 0)
|
||||
{
|
||||
foreach (var created in createdLayers)
|
||||
{
|
||||
if (created.Layer == null)
|
||||
continue;
|
||||
|
||||
var layerDataType = created.Layer.LayerData.GetType();
|
||||
if (LayerHandlers.TryGetValue(layerDataType, out ILayerHandler handler))
|
||||
{
|
||||
s_OpenXRLayerProviderCreate.Begin();
|
||||
handler?.CreateLayer(created);
|
||||
s_OpenXRLayerProviderCreate.End();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modifiedLayers != null && modifiedLayers.Count != 0)
|
||||
{
|
||||
foreach (var modified in modifiedLayers)
|
||||
{
|
||||
if (modified.Layer == null)
|
||||
continue;
|
||||
|
||||
var layerDataType = modified.Layer.LayerData.GetType();
|
||||
|
||||
if (LayerHandlers.TryGetValue(layerDataType, out ILayerHandler handler))
|
||||
{
|
||||
s_OpenXRLayerProviderModify.Begin();
|
||||
handler?.ModifyLayer(modified);
|
||||
s_OpenXRLayerProviderModify.End();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (activeLayers != null && activeLayers.Count != 0)
|
||||
{
|
||||
foreach (var active in activeLayers)
|
||||
{
|
||||
if (active.Layer == null)
|
||||
continue;
|
||||
|
||||
var layerDataType = active.Layer.LayerData.GetType();
|
||||
if (LayerHandlers.TryGetValue(layerDataType, out ILayerHandler handler))
|
||||
{
|
||||
s_OpenXRLayerProviderActive.Begin();
|
||||
handler?.SetActiveLayer(active);
|
||||
s_OpenXRLayerProviderActive.End();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var handler in LayerHandlers.Values)
|
||||
{
|
||||
s_OpenXRLayerProviderUpdate.Begin();
|
||||
handler?.OnUpdate();
|
||||
s_OpenXRLayerProviderUpdate.End();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for cleanup and to call Dispose() on registered layer handlers.
|
||||
/// </summary>
|
||||
/// <remarks>This is called by the OpenXRLoader class when StopInternal() is invoked.</remarks>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var handler in LayerHandlers.Values)
|
||||
{
|
||||
if (handler is IDisposable)
|
||||
{
|
||||
((IDisposable)handler)?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
LayerHandlers.Clear();
|
||||
isStarted = false;
|
||||
Stopped?.Invoke();
|
||||
}
|
||||
|
||||
public void CleanupState()
|
||||
{
|
||||
}
|
||||
|
||||
public void LateUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
void InitializeAndRegisterBuiltInHandlers()
|
||||
{
|
||||
var defaultLayerHandler = new OpenXRDefaultLayer();
|
||||
var quadLayerHandler = new OpenXRQuadLayer();
|
||||
var projectionLayerHandler = new OpenXRProjectionLayer();
|
||||
var cylinderLayerHandler = OpenXRCylinderLayer.ExtensionEnabled ? new OpenXRCylinderLayer() : null;
|
||||
var cubeLayerHandler = OpenXRCubeLayer.ExtensionEnabled ? new OpenXRCubeLayer() : null;
|
||||
ILayerHandler equirectLayerHandler = OpenXREquirect2Layer.ExtensionEnabled ? new OpenXREquirect2Layer() : OpenXREquirectLayer.ExtensionEnabled ? new OpenXREquirectLayer() : null;
|
||||
|
||||
RegisterLayerHandler(typeof(DefaultLayerData), defaultLayerHandler);
|
||||
RegisterLayerHandler(typeof(QuadLayerData), quadLayerHandler);
|
||||
RegisterLayerHandler(typeof(CylinderLayerData), cylinderLayerHandler);
|
||||
RegisterLayerHandler(typeof(ProjectionLayerData), projectionLayerHandler);
|
||||
RegisterLayerHandler(typeof(ProjectionLayerRigData), projectionLayerHandler);
|
||||
RegisterLayerHandler(typeof(CubeProjectionLayerData), cubeLayerHandler);
|
||||
RegisterLayerHandler(typeof(EquirectMeshLayerData), equirectLayerHandler);
|
||||
|
||||
isStarted = true;
|
||||
Started?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d72c58aa04070824aa72a1425788ddbb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,340 @@
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Unity.XR.CompositionLayers;
|
||||
using Unity.XR.CompositionLayers.Extensions;
|
||||
using Unity.XR.CompositionLayers.Services;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.CompositionLayers
|
||||
{
|
||||
/// <summary>
|
||||
/// A general-purpose helper class for composition layer support.
|
||||
/// </summary>
|
||||
public static class OpenXRLayerUtility
|
||||
{
|
||||
internal unsafe delegate void LayerCallbackDelegate(int layerId, XrCompositionLayerBaseHeader* layer);
|
||||
|
||||
static Dictionary<UInt32, RenderTexture> _textureMap = new Dictionary<UInt32, RenderTexture>();
|
||||
|
||||
/// <summary>
|
||||
/// Calls the methods in its invocation list when a swapchain is created on the graphics thread inside the UnityOpenXR lib.
|
||||
/// </summary>
|
||||
/// <param name="layerId">The instance id of the composition layer object.</param>
|
||||
/// <param name="swapchainHandle">The handle to the native swapchain that was just created.</param>
|
||||
public unsafe delegate void SwapchainCallbackDelegate(int layerId, ulong swapchainHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Calls the methods in its invocation list when a stereo swapchain is created on the graphics thread inside the UnityOpenXR lib.
|
||||
/// </summary>
|
||||
/// <param name="layerId">The instance id of the composition layer object.</param>
|
||||
/// <param name="swapchainHandleLeft">The handle to one of the stereo swapchains that was just created.</param>
|
||||
/// <param name="swapchainHandleRight">The handle to one of the stereo swapchains that was just created.</param>
|
||||
public unsafe delegate void StereoSwapchainCallbackDelegate(int layerId, ulong swapchainHandleLeft, ulong swapchainHandleRight);
|
||||
|
||||
/// <summary>
|
||||
/// Helper method used to gather the extension components attached to a CompositionLayer GameObject.
|
||||
/// This method chains the native extension struct pointers of those extension components to initialize an OpenXR native object's Next pointer struct chain.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer.</param>
|
||||
/// <param name="extensionTarget"> Represents what part of the composition layer to retrieve extensions for.</param>
|
||||
/// <returns>A pointer to the head of an array of native extension objects that will be associated with a composition layer.</returns>
|
||||
public static unsafe void* GetExtensionsChain(CompositionLayerManager.LayerInfo layerInfo, CompositionLayerExtension.ExtensionTarget extensionTarget)
|
||||
{
|
||||
void* extensionsChainHead = null;
|
||||
void* extensionsChain = null;
|
||||
|
||||
foreach (var extension in layerInfo.Layer.Extensions)
|
||||
{
|
||||
// Skip extension if not enabled or not the intended target.
|
||||
if (!extension.enabled || extension.Target != extensionTarget)
|
||||
continue;
|
||||
|
||||
var extensionNativeStructPtr = extension.GetNativeStructPtr();
|
||||
|
||||
// Skip extension if no native pointer is provided.
|
||||
if (extensionNativeStructPtr == null)
|
||||
continue;
|
||||
|
||||
// Initialize pointer chain if head has not been set.
|
||||
if (extensionsChainHead == null)
|
||||
{
|
||||
extensionsChainHead = extensionNativeStructPtr;
|
||||
extensionsChain = extensionsChainHead;
|
||||
}
|
||||
|
||||
// Chain pointer if head has been initialized.
|
||||
else
|
||||
{
|
||||
((XrBaseInStructure*)extensionsChain)->Next = extensionNativeStructPtr;
|
||||
extensionsChain = extensionNativeStructPtr;
|
||||
}
|
||||
}
|
||||
|
||||
return extensionsChainHead;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method used get the current app space for any native composition layer structs that may require an associated XrSpace.
|
||||
/// </summary>
|
||||
/// <returns>A handle to the current app space.</returns>
|
||||
/// <remarks>Normally used when creating native composition layers.</remarks>
|
||||
public static ulong GetCurrentAppSpace() => Features.OpenXRFeature.Internal_GetAppSpace(out ulong appSpaceId) ? appSpaceId : 0;
|
||||
|
||||
/// <summary>
|
||||
/// Helper method used get the XR session handle for any native composition layer structs that may require an associated XrSession.
|
||||
/// </summary>
|
||||
/// <returns>A handle to the current xr session.</returns>
|
||||
public static ulong GetXRSession() => Features.OpenXRFeature.Internal_GetXRSession(out ulong xrSessionHandle) ? xrSessionHandle : 0;
|
||||
|
||||
/// <summary>
|
||||
/// Create the <see cref="XrSwapchainCreateInfo"/> struct that is passed to OpenXR SDK to create a swapchain.
|
||||
/// </summary>
|
||||
/// <param name="layerId">The instance id of the composition layer object.</param>
|
||||
/// <param name="createInfo">The struct used to create the swapchain.</param>
|
||||
/// <param name="isExternalSurface"> Optional parameter that can be used when an external surface will be used, like when using the Android Surface feature.</param>
|
||||
/// <param name="callback"> Optional parameter that can be used if your composition layer needs to know the handle after swapchain creation.</param>
|
||||
public static void CreateSwapchain(int layerId, XrSwapchainCreateInfo createInfo, bool isExternalSurface = false, SwapchainCallbackDelegate callback = null)
|
||||
{
|
||||
ext_composition_layers_CreateSwapchain(layerId, createInfo, isExternalSurface, callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the <see cref="XrSwapchainCreateInfo"/> struct that is passed to OpenXR SDK to create a swapchain for stereo projection, like Projection layer type.
|
||||
/// </summary>
|
||||
/// <param name="layerId">The instance id of the composition layer object.</param>
|
||||
/// <param name="createInfo">The struct used to create the swapchain.</param>
|
||||
/// <param name="callback"> Optional parameter that can be used if your composition layer needs to know the handles after swapchain creation.</param>
|
||||
public static void CreateStereoSwapchain(int layerId, XrSwapchainCreateInfo createInfo, StereoSwapchainCallbackDelegate callback = null)
|
||||
{
|
||||
ext_composition_layers_CreateStereoSwapchain(layerId, createInfo, callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release swapchain according to the id provided.
|
||||
/// </summary>
|
||||
/// <param name="layerId">The instance id of the composition layer object.</param>
|
||||
public static void ReleaseSwapchain(int layerId)
|
||||
{
|
||||
ext_composition_layers_ReleaseSwapchain(layerId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return swapchain supported color format.
|
||||
/// </summary>
|
||||
/// <returns>The color format the swapchains will be using.</returns>
|
||||
public static Int64 GetDefaultColorFormat()
|
||||
{
|
||||
return ext_composition_layers_GetUnityDefaultColorFormat();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the render texture of the give texture id.
|
||||
/// </summary>
|
||||
/// <param name="texId">The id of the render texture to find.</param>
|
||||
/// <returns>The render texture with the provided id or null if no render textrue with that id was found.</returns>
|
||||
public static RenderTexture FindRenderTexture(UInt32 texId)
|
||||
{
|
||||
// texId will be 0 if swapchain has no images.
|
||||
if (texId == 0)
|
||||
return null;
|
||||
|
||||
if (!_textureMap.TryGetValue(texId, out var renderTexture))
|
||||
{
|
||||
var objs = Resources.FindObjectsOfTypeAll<RenderTexture>();
|
||||
var name = $"XR Texture [{texId}]";
|
||||
foreach (var rt in objs)
|
||||
{
|
||||
if (rt.name == name)
|
||||
{
|
||||
renderTexture = rt;
|
||||
_textureMap[texId] = rt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return renderTexture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the render texture of the layer id.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer.</param>
|
||||
/// <returns>The render texture with the provided id or null if no render textrue with that id was found.</returns>
|
||||
public static RenderTexture FindRenderTexture(CompositionLayerManager.LayerInfo layerInfo)
|
||||
{
|
||||
UInt32 texId = ext_compositor_layers_CreateOrGetRenderTextureId(layerInfo.Id);
|
||||
return FindRenderTexture(texId);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handles transfering texture data to a render texture.
|
||||
/// </summary>
|
||||
/// <param name="texture">The source texture that will be written into the provided render texture.</param>
|
||||
/// <param name="renderTexture">The render texture that will be written to.</param>
|
||||
public static void WriteToRenderTexture(Texture texture, RenderTexture renderTexture)
|
||||
{
|
||||
if (texture == null || renderTexture == null)
|
||||
return;
|
||||
|
||||
if (renderTexture.dimension == Rendering.TextureDimension.Cube && texture.dimension == Rendering.TextureDimension.Cube)
|
||||
{
|
||||
Cubemap convertedTexture = null;
|
||||
|
||||
// Check if graphics format or mipmap count is different and convert if necessary.
|
||||
if (renderTexture.graphicsFormat != texture.graphicsFormat || renderTexture.mipmapCount != texture.mipmapCount)
|
||||
{
|
||||
convertedTexture = new Cubemap(texture.width, renderTexture.graphicsFormat, Experimental.Rendering.TextureCreationFlags.None);
|
||||
|
||||
// Convert the texture to the render texture format.
|
||||
if (!Graphics.ConvertTexture(texture, convertedTexture))
|
||||
{
|
||||
Debug.LogError("Failed to convert Cubemap to Render Texture format!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
Graphics.CopyTexture(convertedTexture == null ? texture : convertedTexture, i, renderTexture, i);
|
||||
}
|
||||
else
|
||||
Graphics.Blit(texture, renderTexture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query the correct XR Textures for rendering and blit the layer textures.
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer.</param>
|
||||
/// <param name="texture">The source texture that will be written into the provided render texture.</param>
|
||||
/// <param name="renderTexture">The render texture that will be searched for and written to.
|
||||
/// Will be null if no render texture can be found for the provided layerInfo object.</param>
|
||||
/// <returns>True if a render texture was found and written to, false if the provided texture is null or if no render texture was found for the provided layerInfo object.</returns>
|
||||
public static bool FindAndWriteToRenderTexture(CompositionLayerManager.LayerInfo layerInfo, Texture texture, out RenderTexture renderTexture)
|
||||
{
|
||||
if (texture == null)
|
||||
{
|
||||
renderTexture = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
renderTexture = FindRenderTexture(layerInfo);
|
||||
WriteToRenderTexture(texture, renderTexture);
|
||||
return renderTexture != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query the correct XR Textures for rendering and blit the layer textures (For Projection Layer type).
|
||||
/// </summary>
|
||||
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer.</param>
|
||||
/// <param name="renderTextureLeft">The left stereo render texture that will be searched for and written to.
|
||||
/// Will be null if no render textures can be found for the provided layerInfo object.</param>
|
||||
/// <param name="renderTextureRight">The right stereo render texture that will be searched for and written to.
|
||||
/// Will be null if no render textures can be found for the provided layerInfo object.</param>
|
||||
/// <returns>True if both render textures were found and written to, false if no texture was found on the TexturesExtension component of the layerInfo object or if no render texture was found for the provided layerInfo object.</returns>
|
||||
|
||||
public static bool FindAndWriteToStereoRenderTextures(CompositionLayerManager.LayerInfo layerInfo, out RenderTexture renderTextureLeft, out RenderTexture renderTextureRight)
|
||||
{
|
||||
var tex = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (tex == null || tex.LeftTexture == null || tex.RightTexture == null)
|
||||
{
|
||||
renderTextureLeft = null;
|
||||
renderTextureRight = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
ext_compositor_layers_CreateOrGetStereoRenderTextureIds(layerInfo.Id, out uint leftId, out uint rightId);
|
||||
renderTextureLeft = FindRenderTexture(leftId);
|
||||
renderTextureRight = FindRenderTexture(rightId);
|
||||
WriteToRenderTexture(tex.LeftTexture, renderTextureLeft);
|
||||
WriteToRenderTexture(tex.RightTexture, renderTextureRight);
|
||||
return renderTextureLeft != null && renderTextureRight != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add native layer structs to the <c>endFrameInfo</c> struct inside the UnityOpenXR lib - for custom layer type support
|
||||
/// </summary>
|
||||
/// <param name="layers">Pointer to the native array of currently active composition layers.</param>
|
||||
/// <param name="orders">Pointer to the native array of order values for the currently active composition layers.</param>
|
||||
/// <param name="count">Indicates the size of the layers and orders arrays.</param>
|
||||
/// <param name="layerByteSize">Indicates the size in bytes of a single element of the given array of composition layers.</param>
|
||||
/// <remarks>Layers sent must all be of the same type.Demonstrated in the OpenXRCustomLayerHandler class.</remarks>
|
||||
public static unsafe void AddActiveLayersToEndFrame(void* layers, void* orders, int count, int layerByteSize)
|
||||
{
|
||||
ext_composition_layers_AddActiveLayers(layers, orders, count, layerByteSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the Surface object for Android External Surface support (Android only).
|
||||
/// </summary>
|
||||
/// <param name="layerId">The instance id of the composition layer object.</param>
|
||||
/// <returns>Pointer to the android surface object.</returns>
|
||||
public static System.IntPtr GetLayerAndroidSurfaceObject(int layerId)
|
||||
{
|
||||
IntPtr surfaceObject = IntPtr.Zero;
|
||||
if (ext_composition_layers_GetLayerAndroidSurfaceObject(layerId, ref surfaceObject))
|
||||
{
|
||||
return surfaceObject;
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an array of extensions to be attached to the native default compostion layer.
|
||||
/// </summary>
|
||||
/// <param name="extensions">Pointer to the array of extensions to attach to the default compostion layer.</param>
|
||||
/// <remarks>Currently only called by the OpenXRDefautLayer class.</remarks>
|
||||
public static unsafe void SetDefaultSceneLayerExtensions(void* extensions)
|
||||
{
|
||||
ext_composition_layers_SetDefaultSceneLayerExtensions(extensions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends what flags are to be added to the native default compostion layer.
|
||||
/// </summary>
|
||||
/// <param name="flags">Flags to be added to the native default compostion layer.</param>
|
||||
/// <remarks>Currently only called by the OpenXRDefautLayer class.</remarks>
|
||||
public static unsafe void SetDefaultLayerFlags(XrCompositionLayerFlags flags)
|
||||
{
|
||||
ext_composition_layers_SetDefaultSceneLayerFlags(flags);
|
||||
}
|
||||
|
||||
const string LibraryName = "UnityOpenXR";
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern UInt32 ext_compositor_layers_CreateOrGetRenderTextureId(int id);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool ext_compositor_layers_CreateOrGetStereoRenderTextureIds(int id, out UInt32 leftId, out UInt32 rightId);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void ext_composition_layers_CreateSwapchain(int id, XrSwapchainCreateInfo createInfo, [MarshalAs(UnmanagedType.I1)]bool isExternalSurface = false, SwapchainCallbackDelegate callback = null);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void ext_composition_layers_CreateStereoSwapchain(int id, XrSwapchainCreateInfo createInfo, StereoSwapchainCallbackDelegate callback = null);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern Int64 ext_composition_layers_GetUnityDefaultColorFormat();
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void ext_composition_layers_ReleaseSwapchain(int id);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern unsafe void ext_composition_layers_AddActiveLayers(void* layers, void* orders, int count, int size);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool ext_composition_layers_GetLayerAndroidSurfaceObject(int layerId, ref IntPtr surfaceObject);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern unsafe void ext_composition_layers_SetDefaultSceneLayerExtensions(void* extensions);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void ext_composition_layers_SetDefaultSceneLayerFlags(XrCompositionLayerFlags flags);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2efbbbd0477445382219aa4c87a1f00
|
||||
timeCreated: 1667257119
|
||||
@@ -0,0 +1,399 @@
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Unity.Collections;
|
||||
using Unity.XR.CompositionLayers;
|
||||
using Unity.XR.CompositionLayers.Extensions;
|
||||
using Unity.XR.CompositionLayers.Layers;
|
||||
using Unity.XR.CompositionLayers.Services;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.CompositionLayers
|
||||
{
|
||||
//Default OpenXR Composition Layer - Projection Layer support
|
||||
internal class OpenXRProjectionLayer : OpenXRCustomLayerHandler<XrCompositionLayerProjection>, IDisposable
|
||||
{
|
||||
class ProjectionData : IDisposable
|
||||
{
|
||||
public CompositionLayerManager.LayerInfo layer;
|
||||
public NativeArray<XrCompositionLayerProjectionView> nativeViews;
|
||||
public RenderTexture[] renderTextures;
|
||||
public Camera[] cameras;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
nativeViews.Dispose();
|
||||
|
||||
if (renderTextures != null)
|
||||
{
|
||||
foreach (var renderTexture in renderTextures)
|
||||
{
|
||||
renderTexture.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<int, ProjectionData> m_ProjectionData = new Dictionary<int, ProjectionData>();
|
||||
bool m_isMainCameraRendered = false;
|
||||
bool m_isOnBeforeRender = false;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for OpenXR Projection Layer.
|
||||
/// </summary>
|
||||
public OpenXRProjectionLayer()
|
||||
{
|
||||
Application.onBeforeRender += OnBeforeRender;
|
||||
RenderPipelineManager.endCameraRendering += OnCameraEndRendering;
|
||||
Camera.onPostRender += OnCameraPostRender;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to clean up.
|
||||
/// </summary>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (var projectionData in m_ProjectionData.Values)
|
||||
projectionData.Dispose();
|
||||
m_ProjectionData.Clear();
|
||||
}
|
||||
|
||||
Application.onBeforeRender -= OnBeforeRender;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
protected override bool CreateSwapchain(CompositionLayerManager.LayerInfo layer, out SwapchainCreateInfo swapchainCreateInfo)
|
||||
{
|
||||
bool result = ext_composition_layers_GetViewConfigurationData(out uint width, out uint height, out uint sampleCount);
|
||||
if (!result)
|
||||
{
|
||||
swapchainCreateInfo = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var isProjectionRig = layer.Layer.LayerData.GetType() == typeof(ProjectionLayerRigData);
|
||||
if (!isProjectionRig)
|
||||
{
|
||||
TexturesExtension texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null || texturesExtension.RightTexture == null)
|
||||
{
|
||||
swapchainCreateInfo = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
var xrCreateInfo = new XrSwapchainCreateInfo()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
|
||||
CreateFlags = 0,
|
||||
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
|
||||
Format = OpenXRLayerUtility.GetDefaultColorFormat(),
|
||||
SampleCount = sampleCount,
|
||||
Width = width,
|
||||
Height = height,
|
||||
FaceCount = 1,
|
||||
ArraySize = 1,
|
||||
MipCount = 1,
|
||||
};
|
||||
|
||||
swapchainCreateInfo = new SwapchainCreateInfo(xrCreateInfo, isStereo: true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the <see cref="OpenXRLayerProvider"/> when a new <see cref="LayerData"/>
|
||||
/// object of the type registered to this <c>ILayerHandler</c> instance has been created.
|
||||
/// </summary>
|
||||
protected override unsafe bool CreateNativeLayer(CompositionLayerManager.LayerInfo layer, SwapchainCreatedOutput swapchainCreatedOutput, out XrCompositionLayerProjection nativeLayer)
|
||||
{
|
||||
if (layer.Layer == null)
|
||||
{
|
||||
nativeLayer = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = ext_composition_layers_GetViewConfigurationData(out uint width, out uint height, out uint sampleCount);
|
||||
if (!result)
|
||||
{
|
||||
nativeLayer = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var isProjectionRig = layer.Layer.LayerData.GetType() == typeof(ProjectionLayerRigData);
|
||||
if (!isProjectionRig)
|
||||
{
|
||||
TexturesExtension texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null || texturesExtension.RightTexture == null)
|
||||
{
|
||||
nativeLayer = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
XrView xrView1 = default;
|
||||
XrView xrView2 = default;
|
||||
bool validViews = ext_composition_layers_GetStereoViews(&xrView1, &xrView2);
|
||||
XrView[] xrViews = new XrView[2] { xrView1, xrView2 };
|
||||
|
||||
var nativeView1 = new XrCompositionLayerProjectionView()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW,
|
||||
Next = null,
|
||||
Pose = validViews ? xrViews[0].Pose : default,
|
||||
Fov = validViews ? xrViews[0].Fov : default,
|
||||
SubImage = new XrSwapchainSubImage()
|
||||
{
|
||||
Swapchain = swapchainCreatedOutput.handle,
|
||||
ImageRect = new XrRect2Di()
|
||||
{
|
||||
Offset = new XrOffset2Di() { X = 0, Y = 0 },
|
||||
Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = (int)width,
|
||||
Height = (int)height
|
||||
}
|
||||
},
|
||||
ImageArrayIndex = 0
|
||||
},
|
||||
};
|
||||
|
||||
var nativeView2 = new XrCompositionLayerProjectionView()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW,
|
||||
Next = null,
|
||||
Pose = validViews ? xrViews[1].Pose : default,
|
||||
Fov = validViews ? xrViews[1].Fov : default,
|
||||
SubImage = new XrSwapchainSubImage()
|
||||
{
|
||||
Swapchain = swapchainCreatedOutput.secondStereoHandle,
|
||||
ImageRect = new XrRect2Di()
|
||||
{
|
||||
Offset = new XrOffset2Di() { X = 0, Y = 0 },
|
||||
Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = (int)width,
|
||||
Height = (int)height
|
||||
}
|
||||
},
|
||||
ImageArrayIndex = 0
|
||||
},
|
||||
};
|
||||
|
||||
var nativeViews = new NativeArray<XrCompositionLayerProjectionView>(2, Allocator.Persistent);
|
||||
nativeViews[0] = nativeView1;
|
||||
nativeViews[1] = nativeView2;
|
||||
|
||||
nativeLayer = new XrCompositionLayerProjection()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_PROJECTION,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Layer),
|
||||
LayerFlags = XrCompositionLayerFlags.SourceAlpha,
|
||||
Space = OpenXRLayerUtility.GetCurrentAppSpace(),
|
||||
ViewCount = 2,
|
||||
Views = (XrCompositionLayerProjectionView*)nativeViews.GetUnsafePtr()
|
||||
};
|
||||
|
||||
if (!isProjectionRig)
|
||||
{
|
||||
m_ProjectionData.Add(layer.Id, new ProjectionData
|
||||
{
|
||||
layer = layer,
|
||||
nativeViews = nativeViews,
|
||||
renderTextures = null,
|
||||
cameras = null
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create render textures to be the target texture of the child cameras.
|
||||
Camera leftCamera = null;
|
||||
Camera rightCamera = null;
|
||||
if (layer.Layer.transform.childCount >= 2)
|
||||
{
|
||||
leftCamera = layer.Layer.transform.GetChild(0).GetComponent<Camera>();
|
||||
rightCamera = layer.Layer.transform.GetChild(1).GetComponent<Camera>();
|
||||
}
|
||||
|
||||
String layerName = String.Format("projectLayer_{0}", layer.Id);
|
||||
RenderTexture leftRT = new RenderTexture((int)width, (int)height, 0, RenderTextureFormat.ARGB32) {name = layerName + "_left", depthStencilFormat = Experimental.Rendering.GraphicsFormat.D32_SFloat_S8_UInt };
|
||||
leftRT.Create();
|
||||
RenderTexture rightRT = new RenderTexture((int)width, (int)height, 0, RenderTextureFormat.ARGB32) {name = layerName + "_right", depthStencilFormat = Experimental.Rendering.GraphicsFormat.D32_SFloat_S8_UInt };
|
||||
rightRT.Create();
|
||||
|
||||
TexturesExtension texExt = layer.Layer.GetComponent<TexturesExtension>();
|
||||
texExt.LeftTexture = leftRT;
|
||||
texExt.RightTexture = rightRT;
|
||||
|
||||
if (leftCamera)
|
||||
{
|
||||
leftCamera.targetTexture = leftRT;
|
||||
leftCamera.enabled = false;
|
||||
}
|
||||
if (rightCamera != null)
|
||||
{
|
||||
rightCamera.targetTexture = rightRT;
|
||||
rightCamera.enabled = false;
|
||||
}
|
||||
|
||||
m_ProjectionData.Add(layer.Id, new ProjectionData
|
||||
{
|
||||
layer = layer,
|
||||
nativeViews = nativeViews,
|
||||
renderTextures = new RenderTexture[2] { leftRT, rightRT },
|
||||
cameras = new Camera[2] { leftCamera, rightCamera }
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the <see cref="OpenXRLayerProvider"/> when a <see cref="LayerData"/> object
|
||||
/// of the type registered to this <c>ILayerHandler</c> instance has been destroyed or disabled.
|
||||
/// </summary>
|
||||
public override void RemoveLayer(int id)
|
||||
{
|
||||
if (m_ProjectionData.ContainsKey(id))
|
||||
{
|
||||
m_ProjectionData[id].Dispose();
|
||||
m_ProjectionData.Remove(id);
|
||||
}
|
||||
|
||||
base.RemoveLayer(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the <see cref="OpenXRLayerProvider"/> when a <see cref="LayerData"/> object
|
||||
/// or any attached extension components have had a member modified.
|
||||
/// </summary>
|
||||
protected override unsafe bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerProjection nativeLayer)
|
||||
{
|
||||
if (layerInfo.Layer.LayerData is ProjectionLayerRigData && !m_isOnBeforeRender)
|
||||
return false;
|
||||
|
||||
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null || texturesExtension.RightTexture == null)
|
||||
return false;
|
||||
|
||||
XrView view1 = default;
|
||||
XrView view2 = default;
|
||||
bool validViews = ext_composition_layers_GetStereoViews(&view1, &view2);
|
||||
if (validViews)
|
||||
{
|
||||
XrView[] views = new XrView[2] { view1, view2 };
|
||||
nativeLayer.Views[0].Pose = views[0].Pose;
|
||||
nativeLayer.Views[0].Fov = views[0].Fov;
|
||||
nativeLayer.Views[1].Pose = views[1].Pose;
|
||||
nativeLayer.Views[1].Fov = views[1].Fov;
|
||||
}
|
||||
nativeLayer.Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every frame by the <see cref="OpenXRLayerProvider"/> for all currently active <see cref="LayerData"/> objects
|
||||
/// of the type registered to this <c>ILayerHandler</c> instance.
|
||||
/// </summary>
|
||||
protected override unsafe bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerProjection nativeLayer)
|
||||
{
|
||||
if (layerInfo.Layer.LayerData is ProjectionLayerRigData && !m_isOnBeforeRender)
|
||||
return false;
|
||||
|
||||
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension != null || texturesExtension.enabled && texturesExtension.CustomRects && texturesExtension.LeftTexture != null || texturesExtension.RightTexture != null)
|
||||
{
|
||||
nativeLayer.Views[0].SubImage.ImageRect = GetSubImageOffsetAndExtent(texturesExtension.LeftEyeSourceRect, new Vector2(texturesExtension.LeftTexture?.width ?? 0, texturesExtension.LeftTexture?.height ?? 0));
|
||||
nativeLayer.Views[1].SubImage.ImageRect = GetSubImageOffsetAndExtent(texturesExtension.RightEyeSourceRect, new Vector2(texturesExtension.RightTexture?.width ?? 0, texturesExtension.RightTexture?.height ?? 0));
|
||||
}
|
||||
|
||||
nativeLayer.Space = OpenXRLayerUtility.GetCurrentAppSpace();
|
||||
OpenXRLayerUtility.FindAndWriteToStereoRenderTextures(layerInfo, out RenderTexture renderTextureLeft, out RenderTexture renderTextureRight);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Used to hault calls to GetStereoProjectionMatrix before the main camera's projection matrix has been initialized
|
||||
void OnCameraPostRender(Camera cam)
|
||||
{
|
||||
var mainCamera = CompositionLayerManager.mainCameraCache;
|
||||
if (m_isMainCameraRendered || mainCamera == null || cam != mainCamera)
|
||||
return;
|
||||
|
||||
m_isMainCameraRendered = true;
|
||||
Camera.onPostRender -= OnCameraPostRender;
|
||||
RenderPipelineManager.endCameraRendering -= OnCameraEndRendering;
|
||||
}
|
||||
|
||||
// Used to hault calls to GetStereoProjectionMatrix before the main camera's projection matrix has been initialized
|
||||
void OnCameraEndRendering(ScriptableRenderContext cxt, Camera cam) => OnCameraPostRender(cam);
|
||||
|
||||
void OnBeforeRender()
|
||||
{
|
||||
m_isOnBeforeRender = true;
|
||||
UpdateProjectionRigs();
|
||||
m_isOnBeforeRender = false;
|
||||
}
|
||||
|
||||
void UpdateProjectionRigs()
|
||||
{
|
||||
foreach (var projectionData in m_ProjectionData.Values)
|
||||
{
|
||||
// Only projection rig layers will have associated camera data.
|
||||
if (projectionData.cameras == null || projectionData.layer.Layer == null)
|
||||
continue;
|
||||
|
||||
var mainCamera = CompositionLayerManager.mainCameraCache;
|
||||
var leftCamera = projectionData.cameras[0];
|
||||
var rightCamera = projectionData.cameras[1];
|
||||
|
||||
if (m_isMainCameraRendered && leftCamera != null && rightCamera != null)
|
||||
{
|
||||
leftCamera.projectionMatrix = mainCamera.GetStereoProjectionMatrix(Camera.StereoscopicEye.Left);
|
||||
rightCamera.projectionMatrix = mainCamera.GetStereoProjectionMatrix(Camera.StereoscopicEye.Right);
|
||||
|
||||
leftCamera.Render();
|
||||
rightCamera.Render();
|
||||
}
|
||||
|
||||
this.ModifyLayer(projectionData.layer);
|
||||
this.SetActiveLayer(projectionData.layer);
|
||||
}
|
||||
}
|
||||
|
||||
XrRect2Di GetSubImageOffsetAndExtent(Rect sourceRect, Vector2 textureSize)
|
||||
{
|
||||
var sourceTextureOffset = new XrOffset2Di()
|
||||
{
|
||||
X = (int)(sourceRect.x * textureSize.x),
|
||||
Y = (int)(sourceRect.y * textureSize.y)
|
||||
};
|
||||
|
||||
var sourceTextureExtent = new XrExtent2Di()
|
||||
{
|
||||
Width = (int)(sourceRect.width * textureSize.x),
|
||||
Height = (int)(sourceRect.height * textureSize.y)
|
||||
};
|
||||
|
||||
return new XrRect2Di { Offset = sourceTextureOffset, Extent = sourceTextureExtent };
|
||||
}
|
||||
|
||||
[DllImport("UnityOpenXR")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool ext_composition_layers_GetViewConfigurationData(out uint width, out uint height, out uint sampleCount);
|
||||
|
||||
[DllImport("UnityOpenXR")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal unsafe static extern bool ext_composition_layers_GetStereoViews(XrView* view1, XrView* view2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f21e188e121149e695a8c8490aa086dd
|
||||
timeCreated: 1681426276
|
||||
@@ -0,0 +1,299 @@
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
using Unity.XR.CompositionLayers;
|
||||
using Unity.XR.CompositionLayers.Extensions;
|
||||
using Unity.XR.CompositionLayers.Layers;
|
||||
using Unity.XR.CompositionLayers.Services;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.CompositionLayers
|
||||
{
|
||||
//Default OpenXR Composition Layer - Quad Layer support
|
||||
internal class OpenXRQuadLayer : OpenXRCustomLayerHandler<XrCompositionLayerQuad>
|
||||
{
|
||||
protected override bool CreateSwapchain(CompositionLayerManager.LayerInfo layer, out SwapchainCreateInfo swapchainCreateInfo)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false)
|
||||
{
|
||||
swapchainCreateInfo = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (texturesExtension.sourceTexture)
|
||||
{
|
||||
case TexturesExtension.SourceTextureEnum.LocalTexture:
|
||||
{
|
||||
if (texturesExtension.LeftTexture == null)
|
||||
goto default;
|
||||
|
||||
var xrCreateInfo = new XrSwapchainCreateInfo()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
|
||||
CreateFlags = 0,
|
||||
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
|
||||
Format = OpenXRLayerUtility.GetDefaultColorFormat(),
|
||||
SampleCount = 1,
|
||||
Width = (uint)texturesExtension.LeftTexture.width,
|
||||
Height = (uint)texturesExtension.LeftTexture.height,
|
||||
FaceCount = 1,
|
||||
ArraySize = 1,
|
||||
MipCount = (uint)texturesExtension.LeftTexture.mipmapCount,
|
||||
};
|
||||
|
||||
swapchainCreateInfo = new SwapchainCreateInfo(xrCreateInfo, isExternalSurface: false);
|
||||
return true;
|
||||
}
|
||||
|
||||
case TexturesExtension.SourceTextureEnum.AndroidSurface:
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
var xrCreateInfo = new XrSwapchainCreateInfo()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
|
||||
CreateFlags = 0,
|
||||
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
|
||||
Format = 0,
|
||||
SampleCount = 0,
|
||||
Width = (uint)texturesExtension.Resolution.x,
|
||||
Height = (uint)texturesExtension.Resolution.y,
|
||||
FaceCount = 0,
|
||||
ArraySize = 0,
|
||||
MipCount = 0,
|
||||
};
|
||||
|
||||
swapchainCreateInfo = new SwapchainCreateInfo(xrCreateInfo, isExternalSurface: true);
|
||||
return true;
|
||||
#else
|
||||
goto default;
|
||||
#endif
|
||||
}
|
||||
|
||||
default:
|
||||
swapchainCreateInfo = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CreateNativeLayer(CompositionLayerManager.LayerInfo layer, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerQuad nativeLayer)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false)
|
||||
{
|
||||
nativeLayer = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var data = layer.Layer.LayerData as QuadLayerData;
|
||||
var transform = layer.Layer.GetComponent<Transform>();
|
||||
int subImageWidth = 0;
|
||||
int subImageHeight = 0;
|
||||
|
||||
switch (texturesExtension.sourceTexture)
|
||||
{
|
||||
case TexturesExtension.SourceTextureEnum.LocalTexture:
|
||||
{
|
||||
if (texturesExtension.LeftTexture != null)
|
||||
{
|
||||
subImageWidth = texturesExtension.LeftTexture.width;
|
||||
subImageHeight = texturesExtension.LeftTexture.height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TexturesExtension.SourceTextureEnum.AndroidSurface:
|
||||
{
|
||||
subImageWidth = (int)texturesExtension.Resolution.x;
|
||||
subImageHeight = (int)texturesExtension.Resolution.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var correctedSize = texturesExtension.CropToAspect ?
|
||||
FixAspectRatio(data, transform, subImageWidth, subImageHeight) :
|
||||
data.GetScaledSize(transform.lossyScale);
|
||||
|
||||
nativeLayer = new XrCompositionLayerQuad()
|
||||
{
|
||||
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_QUAD,
|
||||
Next = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Layer),
|
||||
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
|
||||
Space = OpenXRLayerUtility.GetCurrentAppSpace(),
|
||||
EyeVisibility = 0,
|
||||
SubImage = new XrSwapchainSubImage()
|
||||
{
|
||||
Swapchain = swapchainOutput.handle,
|
||||
ImageRect = new XrRect2Di()
|
||||
{
|
||||
Offset = new XrOffset2Di() { X = 0, Y = 0 },
|
||||
Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = subImageWidth,
|
||||
Height = subImageHeight
|
||||
}
|
||||
},
|
||||
ImageArrayIndex = 0
|
||||
},
|
||||
Pose = new XrPosef(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation),
|
||||
Size = new XrExtent2Df()
|
||||
{
|
||||
Width = correctedSize.x,
|
||||
Height = correctedSize.y
|
||||
}
|
||||
};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerQuad nativeLayer)
|
||||
{
|
||||
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false)
|
||||
return false;
|
||||
|
||||
var data = layerInfo.Layer.LayerData as QuadLayerData;
|
||||
var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
nativeLayer.Pose = new XrPosef(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
|
||||
|
||||
int subImageWidth = 0;
|
||||
int subImageHeight = 0;
|
||||
|
||||
switch (texturesExtension.sourceTexture)
|
||||
{
|
||||
case TexturesExtension.SourceTextureEnum.LocalTexture:
|
||||
{
|
||||
if (texturesExtension.LeftTexture != null)
|
||||
{
|
||||
subImageWidth = texturesExtension.LeftTexture.width;
|
||||
subImageHeight = texturesExtension.LeftTexture.height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TexturesExtension.SourceTextureEnum.AndroidSurface:
|
||||
{
|
||||
subImageWidth = (int)texturesExtension.Resolution.x;
|
||||
subImageHeight = (int)texturesExtension.Resolution.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nativeLayer.SubImage.ImageRect.Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = subImageWidth,
|
||||
Height = subImageHeight
|
||||
};
|
||||
|
||||
var correctedSize = texturesExtension.CropToAspect ?
|
||||
FixAspectRatio(data, transform, subImageWidth, subImageHeight) :
|
||||
data.GetScaledSize(transform.lossyScale);
|
||||
nativeLayer.Size = new XrExtent2Df()
|
||||
{
|
||||
Width = correctedSize.x,
|
||||
Height = correctedSize.y
|
||||
};
|
||||
|
||||
unsafe
|
||||
{
|
||||
nativeLayer.Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerQuad nativeLayer)
|
||||
{
|
||||
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
|
||||
if (texturesExtension == null || texturesExtension.enabled == false)
|
||||
return false;
|
||||
|
||||
var data = layerInfo.Layer.LayerData as QuadLayerData;
|
||||
var transform = layerInfo.Layer.GetComponent<Transform>();
|
||||
nativeLayer.Pose = new XrPosef(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
|
||||
nativeLayer.Space = OpenXRLayerUtility.GetCurrentAppSpace();
|
||||
|
||||
if (texturesExtension.CustomRects)
|
||||
{
|
||||
int subImageWidth = 0;
|
||||
int subImageHeight = 0;
|
||||
switch (texturesExtension.sourceTexture)
|
||||
{
|
||||
case TexturesExtension.SourceTextureEnum.LocalTexture:
|
||||
{
|
||||
if (texturesExtension.LeftTexture != null)
|
||||
{
|
||||
subImageWidth = texturesExtension.LeftTexture.width;
|
||||
subImageHeight = texturesExtension.LeftTexture.height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TexturesExtension.SourceTextureEnum.AndroidSurface:
|
||||
{
|
||||
subImageWidth = (int)texturesExtension.Resolution.x;
|
||||
subImageHeight = (int)texturesExtension.Resolution.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nativeLayer.SubImage.ImageRect = new XrRect2Di()
|
||||
{
|
||||
Offset = new XrOffset2Di()
|
||||
{
|
||||
X = (int)(subImageWidth * texturesExtension.LeftEyeSourceRect.x),
|
||||
Y = (int)(subImageHeight * texturesExtension.LeftEyeSourceRect.y)
|
||||
},
|
||||
|
||||
Extent = new XrExtent2Di()
|
||||
{
|
||||
Width = (int)(subImageWidth * texturesExtension.LeftEyeSourceRect.width),
|
||||
Height = (int)(subImageHeight * texturesExtension.LeftEyeSourceRect.height)
|
||||
}
|
||||
};
|
||||
|
||||
var currentPosition = OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position;
|
||||
var correctedSize = texturesExtension.CropToAspect ?
|
||||
FixAspectRatio(data, transform, subImageWidth, subImageHeight) :
|
||||
data.GetScaledSize(transform.lossyScale);
|
||||
|
||||
float transformedX = currentPosition.x + (((texturesExtension.LeftEyeDestinationRect.x + (0.5f * texturesExtension.LeftEyeDestinationRect.width) - 0.5f)) * correctedSize.x);
|
||||
float transformedY = currentPosition.y + (((texturesExtension.LeftEyeDestinationRect.y + (0.5f * texturesExtension.LeftEyeDestinationRect.height) - 0.5f)) * (-1.0f * correctedSize.y));
|
||||
nativeLayer.Pose = new XrPosef(new Vector3(transformedX, transformedY, currentPosition.z), OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
|
||||
nativeLayer.Size = new XrExtent2Df()
|
||||
{
|
||||
Width = correctedSize.x * texturesExtension.LeftEyeDestinationRect.width,
|
||||
Height = correctedSize.y * texturesExtension.LeftEyeDestinationRect.height
|
||||
};
|
||||
}
|
||||
|
||||
return base.ActiveNativeLayer(layerInfo, ref nativeLayer);
|
||||
}
|
||||
|
||||
static Vector2 FixAspectRatio(QuadLayerData data, Transform transform, int texWidth, int texHeight)
|
||||
{
|
||||
var requestedSize = data.GetScaledSize(transform.lossyScale);
|
||||
float reqSizeRatio = (float)requestedSize.x / (float)requestedSize.y;
|
||||
float texRatio = (float)texWidth / (float)texHeight;
|
||||
if (reqSizeRatio > texRatio)
|
||||
{
|
||||
// too wide
|
||||
requestedSize.x = requestedSize.y * texRatio;
|
||||
}
|
||||
else if (reqSizeRatio < texRatio)
|
||||
{
|
||||
// too narrow
|
||||
requestedSize.y = requestedSize.x / texRatio;
|
||||
}
|
||||
return requestedSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc3d136d6dda4723899962838c66fbe9
|
||||
timeCreated: 1667255803
|
||||
8
Packages/com.unity.xr.openxr/Runtime/Features.meta
Normal file
8
Packages/com.unity.xr.openxr/Runtime/Features.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a35b71692a8a6724e89cc63c9a2ca07b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 632a91c30693a654d80b70b28e764d3d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,134 @@
|
||||
using System.Runtime.InteropServices;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Extensions.PerformanceSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows interaction with the Performance settings API,
|
||||
/// which allows the application to provide hints to the runtime about the performance characteristics of the application,
|
||||
/// and to receive notifications from the runtime about changes in performance state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Refer to [XR performance settings](xref:openxr-performance-settings) for additional information.
|
||||
/// </remarks>
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(
|
||||
UiName = "XR Performance Settings",
|
||||
Desc = "Optional extension for providing performance hints to runtime and receive notifications aobut device performance changes.",
|
||||
Company = "Unity",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/performance-settings.html",
|
||||
OpenxrExtensionStrings = extensionString,
|
||||
Version = "1.0.0",
|
||||
FeatureId = featureId
|
||||
)]
|
||||
#endif
|
||||
public class XrPerformanceSettingsFeature : OpenXRFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.extension.performance_settings";
|
||||
|
||||
/// <summary>
|
||||
/// Name of the OpenXR extension for performance settings.
|
||||
/// </summary>
|
||||
public const string extensionString = "XR_EXT_performance_settings";
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to this event to receive performance change notifications from the OpenXR runtime.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Refer to [Performance notifications](xref:openxr-performance-settings#performance-settings-notifications) for additional information.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para>
|
||||
/// Example of subscribing to the event and handling the performance change notification:
|
||||
/// </para>
|
||||
/// <code lang="cs">
|
||||
/// <![CDATA[
|
||||
/// XrPerformanceSettingsFeature.OnXrPerformanceChangeNotification += OnGPUPerformanceChange;
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// <para>
|
||||
/// Example of handling the performance change notification:
|
||||
/// </para>
|
||||
/// <code lang="cs">
|
||||
/// <![CDATA[
|
||||
/// void OnGPUPerformanceChange(PerformanceChangeNotification notification)
|
||||
/// {
|
||||
/// if (notification.domain != Domain.GPU)
|
||||
/// {
|
||||
/// return;
|
||||
/// }
|
||||
///
|
||||
/// if (notification.toLevel == PerformanceNotificationLevel.Normal)
|
||||
/// {
|
||||
/// // Performance has improved
|
||||
/// UseDefaultQuality();
|
||||
/// }
|
||||
/// else
|
||||
/// {
|
||||
/// // Performance has degraded
|
||||
/// UseReducedQuality();
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static event UnityAction<PerformanceChangeNotification> OnXrPerformanceChangeNotification;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the OpenXR runtime with the desired performance level to be used for the specified domain.
|
||||
/// </summary>
|
||||
/// <param name="domain">Domain for which the performance hit will be sent.</param>
|
||||
/// <param name="level">Desired performance asked by the application.</param>
|
||||
/// <returns>True if the performance level hint was successfully set, false otherwise.</returns>
|
||||
/// <remarks>
|
||||
/// Refer to [Performance level hints](xref: openxr-performance-settings#performance-settings-level-hints) for additional information.
|
||||
/// </remarks>
|
||||
public static bool SetPerformanceLevelHint(PerformanceDomain domain, PerformanceLevelHint level)
|
||||
{
|
||||
if (OpenXRRuntime.IsExtensionEnabled(extensionString))
|
||||
{
|
||||
return NativeApi.xr_performance_settings_setPerformanceLevel(domain, level);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When an instance of the Performance setting feature is created, it allows us to confirm that the instance has been created, that the extension is enabled
|
||||
/// and we have successfully registed the performance notification callback
|
||||
/// </summary>
|
||||
/// <param name="xrInstance">XR Session Instance</param>
|
||||
/// <returns>True if the instance has successfully been created. Otherwise it returns false.</returns>
|
||||
protected internal override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
return base.OnInstanceCreate(xrInstance) &&
|
||||
OpenXRRuntime.IsExtensionEnabled(extensionString) &&
|
||||
NativeApi.xr_performance_settings_setEventCallback(OnXrPerformanceNotificationCallback);
|
||||
}
|
||||
|
||||
[AOT.MonoPInvokeCallback(typeof(NativeApi.XrPerformanceNotificationDelegate))]
|
||||
private static void OnXrPerformanceNotificationCallback(PerformanceChangeNotification notification)
|
||||
{
|
||||
OnXrPerformanceChangeNotification?.Invoke(notification);
|
||||
}
|
||||
|
||||
internal static class NativeApi
|
||||
{
|
||||
internal delegate void XrPerformanceNotificationDelegate(PerformanceChangeNotification notification);
|
||||
|
||||
[DllImport("UnityOpenXR")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool xr_performance_settings_setEventCallback(XrPerformanceNotificationDelegate callback);
|
||||
|
||||
[DllImport("UnityOpenXR")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool xr_performance_settings_setPerformanceLevel(PerformanceDomain domain, PerformanceLevelHint level);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b213d3e3c7f3109449eb46a4c8ee42f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,133 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Extensions.PerformanceSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Hardware system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use the members of this enumeration when setting a performance hint with <see cref="XRPerformanceSettingsFeature.SetPerformanceLevelHint(PerformanceDomain, PerformanceLevelHint)"/>.
|
||||
///
|
||||
/// Members of this enumeration are reported in the events dispatched by <see cref="XRPerformanceSettingsFeature.UnityAction{PerformanceChangeNotification} OnXrPerformanceChangeNotification"/>.
|
||||
/// </remarks>
|
||||
public enum PerformanceDomain
|
||||
{
|
||||
/// <summary>
|
||||
/// CPU hardware domain.
|
||||
/// </summary>
|
||||
Cpu = 1,
|
||||
/// <summary>
|
||||
/// Graphics hardware domain.
|
||||
/// </summary>
|
||||
Gpu = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specific context of the hardware domain.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Members of this enumeration are reported in the events dispatched by
|
||||
/// <see cref="XRPerformanceSettingsFeature.UnityAction{PerformanceChangeNotification} OnXrPerformanceChangeNotification"/>.
|
||||
/// </remarks>
|
||||
public enum PerformanceSubDomain
|
||||
{
|
||||
/// <summary>
|
||||
/// Composition of submitted layers.
|
||||
/// </summary>
|
||||
Compositing = 1,
|
||||
/// <summary>
|
||||
/// Graphics rendering and frame submission.
|
||||
/// </summary>
|
||||
Rendering = 2,
|
||||
/// <summary>
|
||||
/// Physical device temperature.
|
||||
/// </summary>
|
||||
Thermal = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performance level of the platform.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use the members of this enumeration when setting a performance hint with
|
||||
/// <see cref="XRPerformanceSettingsFeature.SetPerformanceLevelHint(PerformanceDomain, PerformanceLevelHint)"/>.
|
||||
/// </remarks>
|
||||
public enum PerformanceLevelHint
|
||||
{
|
||||
/// <summary>
|
||||
/// The application will enter a non-XR section,
|
||||
/// so power savings will be prioritized over all.
|
||||
/// </summary>
|
||||
PowerSavings = 0,
|
||||
/// <summary>
|
||||
/// The application will enter a low and stable complexity section,
|
||||
/// so power usage will be prioritized over late frame rendering.
|
||||
/// </summary>
|
||||
SustainedLow = 25,
|
||||
/// <summary>
|
||||
/// The application will enter a high or dynamic complexity section,
|
||||
/// so the application performance will be prioritized within sustainable thermal ranges.
|
||||
/// </summary>
|
||||
SustainedHigh = 50,
|
||||
/// <summary>
|
||||
/// The application will enter a very high complexity section,
|
||||
/// so performance will be boosted over sustainable thermal ranges.
|
||||
/// Note that usage of this level hint is recommended for short durations (less than 30 seconds).
|
||||
/// </summary>
|
||||
Boost = 75
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notification level about the performance state of the platform.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Members of this enumeration are reported in the events dispatched by
|
||||
/// <see cref="XRPerformanceSettingsFeature.UnityAction{PerformanceChangeNotification} OnXrPerformanceChangeNotification"/>.
|
||||
/// </remarks>
|
||||
public enum PerformanceNotificationLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Performance is in nominal status.
|
||||
/// </summary>
|
||||
Normal = 0,
|
||||
/// <summary>
|
||||
/// Early warning for potential performance degradation.
|
||||
/// </summary>
|
||||
Warning = 25,
|
||||
/// <summary>
|
||||
/// Performance is degraded.
|
||||
/// </summary>
|
||||
Impaired = 75
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notification about the performance state of the platform.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is part of the events dispatched by
|
||||
/// <see cref="XRPerformanceSettingsFeature.UnityAction{PerformanceChangeNotification} OnXrPerformanceChangeNotification"/>.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PerformanceChangeNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// Platform domain that the notification is about.
|
||||
/// </summary>
|
||||
public PerformanceDomain domain;
|
||||
/// <summary>
|
||||
/// Platform subdomain that the notification is about.
|
||||
/// </summary>
|
||||
public PerformanceSubDomain subDomain;
|
||||
/// <summary>
|
||||
/// Previous performance level.
|
||||
/// </summary>
|
||||
public PerformanceNotificationLevel fromLevel;
|
||||
/// <summary>
|
||||
/// Upcoming performance level.
|
||||
/// </summary>
|
||||
public PerformanceNotificationLevel toLevel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48fee7f780ee00c44bc696d37673aec9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRFeature"/> enables the use of foveated rendering in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR && UNITY_2023_2_OR_NEWER
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Foveated Rendering",
|
||||
BuildTargetGroups = new []{BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
|
||||
Company = "Unity",
|
||||
Desc = "Add foveated rendering.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/foveatedrendering.html",
|
||||
OpenxrExtensionStrings = "XR_UNITY_foveation XR_FB_foveation XR_FB_foveation_configuration XR_FB_swapchain_update_state XR_FB_foveation_vulkan XR_META_foveation_eye_tracked XR_META_vulkan_swapchain_create_info",
|
||||
Version = "1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Feature,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class FoveatedRenderingFeature : OpenXRFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.foveatedrendering";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected internal override bool OnInstanceCreate(ulong instance)
|
||||
{
|
||||
// If using BiRP, the feature must know not to use the newer API for FSR/FDM
|
||||
Internal_Unity_SetUseFoveatedRenderingLegacyMode(GraphicsSettings.defaultRenderPipeline == null);
|
||||
return base.OnInstanceCreate(instance);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected internal override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||
{
|
||||
return Internal_Unity_intercept_xrGetInstanceProcAddr(func);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private const string Library = "UnityOpenXR";
|
||||
|
||||
[DllImport(Library, EntryPoint = "UnityFoveation_intercept_xrGetInstanceProcAddr")]
|
||||
private static extern IntPtr Internal_Unity_intercept_xrGetInstanceProcAddr(IntPtr func);
|
||||
|
||||
[DllImport(Library, EntryPoint = "UnityFoveation_SetUseFoveatedRenderingLegacyMode")]
|
||||
private static extern void Internal_Unity_SetUseFoveatedRenderingLegacyMode([MarshalAs(UnmanagedType.I1)] bool value);
|
||||
|
||||
[DllImport(Library, EntryPoint = "UnityFoveation_GetUseFoveatedRenderingLegacyMode")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool Internal_Unity_GetUseFoveatedRenderingLegacyMode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6a75d1f5ff90154ea2a8e58222a1f59
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa266ef62a5d4ede92a5c128da19498c
|
||||
timeCreated: 1606858557
|
||||
@@ -0,0 +1,517 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of DPad feature in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "D-Pad Binding",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
|
||||
Company = "Unity",
|
||||
Desc = "Add DPad feature support and if enabled, extra dpad paths will be added to any controller profiles with a thumbstick or trackpad.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/dpadinteraction.html",
|
||||
OpenxrExtensionStrings = "XR_KHR_binding_modification XR_EXT_dpad_binding",
|
||||
Version = "0.0.1",
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class DPadInteraction : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.dpadinteraction";
|
||||
|
||||
/// <summary>
|
||||
/// A flag to mark this DPad feature is additive.
|
||||
/// </summary>
|
||||
internal override bool IsAdditive => true;
|
||||
|
||||
/// <summary>
|
||||
/// a number in the half-open range (0, 1] representing the force value threshold at or above which ≥ a dpad input will transition from inactive to active.
|
||||
/// </summary>
|
||||
public float forceThresholdLeft = 0.5f;
|
||||
/// <summary>
|
||||
/// a number in the half-open range (0, 1] representing the force value threshold strictly below which less than a dpad input will transition from active to inactive.
|
||||
/// </summary>
|
||||
public float forceThresholdReleaseLeft = 0.4f;
|
||||
/// <summary>
|
||||
/// the radius in the input value space, of a logically circular region in the center of the input, in the range (0, 1).
|
||||
/// </summary>
|
||||
public float centerRegionLeft = 0.5f;
|
||||
/// <summary>
|
||||
/// indicates the angle in radians of each direction region and is a value in the half-open range (0, π].
|
||||
/// </summary>
|
||||
public float wedgeAngleLeft = (float)(0.5f * Math.PI);
|
||||
/// <summary>
|
||||
/// indicates that the implementation will latch the first region that is activated and continue to indicate that the binding for that region is true until the user releases the input underlying the virtual dpad.
|
||||
/// </summary>
|
||||
public bool isStickyLeft = false;
|
||||
|
||||
/// <summary>
|
||||
/// a number in the half-open range (0, 1] representing the force value threshold at or above which ≥ a dpad input will transition from inactive to active.
|
||||
/// </summary>
|
||||
public float forceThresholdRight = 0.5f;
|
||||
/// <summary>
|
||||
/// a number in the half-open range (0, 1] representing the force value threshold strictly below which less than a dpad input will transition from active to inactive.
|
||||
/// </summary>
|
||||
public float forceThresholdReleaseRight = 0.4f;
|
||||
/// <summary>
|
||||
/// the radius in the input value space, of a logically circular region in the center of the input, in the range (0, 1).
|
||||
/// </summary>
|
||||
public float centerRegionRight = 0.5f;
|
||||
/// <summary>
|
||||
/// indicates the angle in radians of each direction region and is a value in the half-open range [0, π].
|
||||
/// </summary>
|
||||
public float wedgeAngleRight = (float)(0.5f * Math.PI);
|
||||
/// <summary>
|
||||
/// indicates that the implementation will latch the first region that is activated and continue to indicate that the binding for that region is true until the user releases the input underlying the virtual dpad.
|
||||
/// </summary>
|
||||
public bool isStickyRight = false;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal class DpadControlEditorWindow : EditorWindow
|
||||
{
|
||||
private Object feature;
|
||||
private Editor featureEditor;
|
||||
|
||||
public static EditorWindow Create(Object feature)
|
||||
{
|
||||
var window = EditorWindow.GetWindow<DpadControlEditorWindow>(true, "Dpad Interaction Binding Setting", true);
|
||||
window.feature = feature;
|
||||
window.featureEditor = Editor.CreateEditor(feature);
|
||||
return window;
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
featureEditor.OnInspectorGUI();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// A dpad-like interaction feature that allows the application to bind one or more digital actions to a trackpad or thumbstick as though it were a dpad. <a href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrInteractionProfileDpadBindingEXT">XrInteractionProfileDpadBindingEXT</a>
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "D-Pad Binding (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class DPad : XRController
|
||||
{
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="DPadInteraction.thumbstickDpadUp"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl()]
|
||||
public ButtonControl thumbstickDpadUp { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="DPadInteraction.thumbstickDpadDown"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl()]
|
||||
public ButtonControl thumbstickDpadDown { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="DPadInteraction.thumbstickDpadLeft"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl()]
|
||||
public ButtonControl thumbstickDpadLeft { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="DPadInteraction.thumbstickDpadRight"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl()]
|
||||
public ButtonControl thumbstickDpadRight { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="DPadInteraction.trackpadDpadUp"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl()]
|
||||
public ButtonControl trackpadDpadUp { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="DPadInteraction.trackpadDpadDown"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl()]
|
||||
public ButtonControl trackpadDpadDown { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="DPadInteractionP.trackpadDpadLeft"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl()]
|
||||
public ButtonControl trackpadDpadLeft { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="DPadInteraction.trackpadDpadRight"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl()]
|
||||
public ButtonControl trackpadDpadRight { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="DPadInteraction.trackpadDpadCenter"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl()]
|
||||
public ButtonControl trackpadDpadCenter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal call used to assign controls to the the correct element.
|
||||
/// </summary>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
thumbstickDpadUp = GetChildControl<ButtonControl>("thumbstickDpadUp");
|
||||
thumbstickDpadDown = GetChildControl<ButtonControl>("thumbstickDpadDown");
|
||||
thumbstickDpadLeft = GetChildControl<ButtonControl>("thumbstickDpadLeft");
|
||||
thumbstickDpadRight = GetChildControl<ButtonControl>("thumbstickDpadRight");
|
||||
trackpadDpadUp = GetChildControl<ButtonControl>("trackpadDpadUp");
|
||||
trackpadDpadDown = GetChildControl<ButtonControl>("trackpadDpadDown");
|
||||
trackpadDpadLeft = GetChildControl<ButtonControl>("trackpadDpadLeft");
|
||||
trackpadDpadRight = GetChildControl<ButtonControl>("trackpadDpadRight");
|
||||
trackpadDpadCenter = GetChildControl<ButtonControl>("trackpadDpadCenter");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../thumbstick/dpad_up' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string thumbstickDpadUp = "/input/thumbstick/dpad_up";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../thumbstick/dpad_down' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string thumbstickDpadDown = "/input/thumbstick/dpad_down";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../thumbstick/dpad_left' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string thumbstickDpadLeft = "/input/thumbstick/dpad_left";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../thumbstick/dpad_right' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string thumbstickDpadRight = "/input/thumbstick/dpad_right";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../trackpad/dpad_up' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string trackpadDpadUp = "/input/trackpad/dpad_up";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../trackpad/dpad_down' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string trackpadDpadDown = "/input/trackpad/dpad_down";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../trackpad/dpad_left' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string trackpadDpadLeft = "/input/trackpad/dpad_left";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../trackpad/dpad_right' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string trackpadDpadRight = "/input/trackpad/dpad_right";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../trackpad/dpad_center' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string trackpadDpadCenter = "/input/trackpad/dpad_center";
|
||||
|
||||
/// <summary>
|
||||
/// A unique string for dpad feature
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/unity/dpad";
|
||||
|
||||
private const string kDeviceLocalizedName = "DPad Interaction OpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// The OpenXR Extension strings. This is used by OpenXR to check if this extension is available or enabled.
|
||||
/// </summary>
|
||||
public string[] extensionStrings = { "XR_KHR_binding_modification", "XR_EXT_dpad_binding" };
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected internal override void GetValidationChecks(List<OpenXRFeature.ValidationRule> results, BuildTargetGroup target)
|
||||
{
|
||||
results.Add( new ValidationRule(this){
|
||||
message = "Additive Interaction feature requires a valid controller profile with thumbstick or trackpad selected within Interaction Profiles.",
|
||||
error = true,
|
||||
errorEnteringPlaymode = true,
|
||||
checkPredicate = () =>
|
||||
{
|
||||
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(target);
|
||||
if (null == settings)
|
||||
return false;
|
||||
|
||||
bool dpadFeatureEnabled = false;
|
||||
bool otherNonAdditiveInteractionFeatureEnabled = false;
|
||||
foreach (var feature in settings.GetFeatures<OpenXRInteractionFeature>())
|
||||
{
|
||||
if (feature.enabled)
|
||||
{
|
||||
if (feature is DPadInteraction)
|
||||
dpadFeatureEnabled = true;
|
||||
else if (!(feature as OpenXRInteractionFeature).IsAdditive && !(feature is EyeGazeInteraction))
|
||||
otherNonAdditiveInteractionFeatureEnabled = true;
|
||||
}
|
||||
}
|
||||
return dpadFeatureEnabled && otherNonAdditiveInteractionFeatureEnabled;
|
||||
},
|
||||
fixIt = () => SettingsService.OpenProjectSettings("Project/XR Plug-in Management/OpenXR"),
|
||||
fixItAutomatic = false,
|
||||
fixItMessage = "Open Project Settings to select one or more non Additive interaction profiles."
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override bool OnInstanceCreate(ulong instance)
|
||||
{
|
||||
// Requires dpad related exts
|
||||
foreach (var ext in extensionStrings)
|
||||
{
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(ext))
|
||||
return false;
|
||||
}
|
||||
return base.OnInstanceCreate(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="DPad"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(DPad),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="DPad"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(DPad));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string for registering Dpad in InputSystem.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(DPad);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "dpadinteraction",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickDpadUp",
|
||||
localizedName = " Thumbstick Dpad Up",
|
||||
type = ActionType.Binary,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickDpadUp,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickDpadDown",
|
||||
localizedName = "Thumbstick Dpad Down",
|
||||
type = ActionType.Binary,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickDpadDown,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickDpadLeft",
|
||||
localizedName = "Thumbstick Dpad Left",
|
||||
type = ActionType.Binary,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickDpadLeft,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickDpadRight",
|
||||
localizedName = "Thumbstick Dpad Right",
|
||||
type = ActionType.Binary,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickDpadRight,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpadDpadUp",
|
||||
localizedName = "Trackpad Dpad Up",
|
||||
type = ActionType.Binary,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadDpadUp,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpadDpadDown",
|
||||
localizedName = "Trackpad Dpad Down",
|
||||
type = ActionType.Binary,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadDpadDown,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpadDpadLeft",
|
||||
localizedName = "Trackpad Dpad Left",
|
||||
type = ActionType.Binary,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadDpadLeft,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpadDpadRight",
|
||||
localizedName = "Trackpad Dpad Right",
|
||||
type = ActionType.Binary,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadDpadRight,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpadDpadCenter",
|
||||
localizedName = "Trackpad Dpad Center",
|
||||
type = ActionType.Binary,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadDpadCenter,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
|
||||
//Process additive actions: add additional supported additive actions to the existing controller profiles
|
||||
internal override void AddAdditiveActions(List<OpenXRInteractionFeature.ActionMapConfig> actionMaps, ActionMapConfig additiveMap)
|
||||
{
|
||||
foreach (var actionMap in actionMaps)
|
||||
{
|
||||
//valid userPath is user/hand/left or user/hand/right
|
||||
var validUserPath = actionMap.deviceInfos.Where(d => d.userPath != null && ((String.CompareOrdinal(d.userPath, OpenXRInteractionFeature.UserPaths.leftHand) == 0) ||
|
||||
(String.CompareOrdinal(d.userPath, OpenXRInteractionFeature.UserPaths.rightHand) == 0)));
|
||||
if (!validUserPath.Any())
|
||||
continue;
|
||||
|
||||
//check if interaction profile has thumbstick and/or trackpad
|
||||
bool hasTrackPad = false;
|
||||
bool hasThumbstick = false;
|
||||
foreach (var action in actionMap.actions)
|
||||
{
|
||||
if (!hasTrackPad)
|
||||
{
|
||||
var withTrackpad = action.bindings.FirstOrDefault(b => b.interactionPath.Contains("trackpad"));
|
||||
if (withTrackpad != null)
|
||||
hasTrackPad = true;
|
||||
}
|
||||
if (!hasThumbstick)
|
||||
{
|
||||
var withThumbstick = action.bindings.FirstOrDefault(b => b.interactionPath.Contains("thumbstick"));
|
||||
if (withThumbstick != null)
|
||||
hasThumbstick = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var additiveAction in additiveMap.actions.Where(a => a.isAdditive))
|
||||
{
|
||||
if ((hasTrackPad && additiveAction.name.StartsWith("trackpad")) || (hasThumbstick && additiveAction.name.StartsWith("thumbstick")))
|
||||
actionMap.actions.Add(additiveAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c5b5af5107e35a43818d5411328bfc3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,210 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of eye gaze interaction profiles in OpenXR. It enables <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_eye_gaze_interaction">XR_EXT_eye_gaze_interaction</a> in the underlying runtime.
|
||||
/// This creates a new <see cref="InputDevice"/> with the <see cref="InputDeviceCharacteristics.EyeTracking"/> characteristic. This new device has both <see cref="EyeTrackingUsages.gazePosition"/> and <see cref="EyeTrackingUsages.gazeRotation"/> input features, as well as <see cref="CommonUsages.isTracked"/> and <see cref="CommonUsages.trackingState"/> usages to determine if the gaze is available.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Eye Gaze Interaction Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.WSA, BuildTargetGroup.Standalone, BuildTargetGroup.Android },
|
||||
Company = "Unity",
|
||||
Desc = "Support for enabling the eye tracking interaction profile. Will register the controller map for eye tracking if enabled.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/eyegazeinteraction.html",
|
||||
Version = "0.0.1",
|
||||
OpenxrExtensionStrings = extensionString,
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class EyeGazeInteraction : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.eyetracking";
|
||||
|
||||
/// <summary>
|
||||
/// An Input System device based off the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#_eye_gaze_input">Eye Gaze Interaction Profile</a>. Enabled through <see cref="EyeGazeInteraction"/>.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Eye Gaze (OpenXR)", isGenericTypeOfDevice = true)]
|
||||
public class EyeGazeDevice : OpenXRDevice
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> representing the <see cref="EyeGazeInteraction.pose"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, usages = new[] { "Device", "gaze" })]
|
||||
public PoseControl pose { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
pose = GetChildControl<PoseControl>("pose");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The OpenXR constant that is used to reference an eye tracking supported input device. See <see href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-user">OpenXR Specification 6.3.1</see> for more information on user paths.
|
||||
/// </summary>
|
||||
private const string userPath = "/user/eyes_ext";
|
||||
|
||||
/// <summary>The interaction profile string used to reference the eye gaze input device.</summary>
|
||||
private const string profile = "/interaction_profiles/ext/eye_gaze_interaction";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/gaze_ext/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
private const string pose = "/input/gaze_ext/pose";
|
||||
|
||||
private const string kDeviceLocalizedName = "Eye Tracking OpenXR";
|
||||
|
||||
/// <summary>The OpenXR Extension string. This is used by OpenXR to check if this extension is available or enabled. See <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_eye_gaze_interaction">eye gaze interaction extension</a> documentation for more information on this OpenXR extension.</summary>
|
||||
public const string extensionString = "XR_EXT_eye_gaze_interaction";
|
||||
|
||||
private const string layoutName = "EyeGaze";
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected internal override void GetValidationChecks(List<OpenXRFeature.ValidationRule> results, BuildTargetGroup target)
|
||||
{
|
||||
if (target == BuildTargetGroup.WSA)
|
||||
{
|
||||
results.Add(new ValidationRule(this){
|
||||
message = "Eye Gaze support requires the Gaze Input capability.",
|
||||
error = false,
|
||||
checkPredicate = () => PlayerSettings.WSA.GetCapability(PlayerSettings.WSACapability.GazeInput),
|
||||
fixIt = () => PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.GazeInput, true)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override bool OnInstanceCreate(ulong instance)
|
||||
{
|
||||
// Requires the eye tracking extension
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(extensionString))
|
||||
return false;
|
||||
|
||||
return base.OnInstanceCreate(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="EyeGazeDevice"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(EyeGazeDevice),
|
||||
layoutName,
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="EyeGazeDevice"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(layoutName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return interaction profile type. EyeGaze profile is Device type.
|
||||
/// </summary>
|
||||
/// <returns>Interaction profile type.</returns>
|
||||
protected override InteractionProfileType GetInteractionProfileType()
|
||||
{
|
||||
return typeof(EyeGazeDevice).IsSubclassOf(typeof(XRController)) ? InteractionProfileType.XRController : InteractionProfileType.Device;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layer out string used for registering device EyeGaze in InputSystem.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return layoutName;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "eyegaze",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.EyeTracking | InputDeviceCharacteristics.HeadMounted,
|
||||
userPath = userPath
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pose",
|
||||
localizedName = "Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device",
|
||||
"gaze"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = pose,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tags that can be used with <see cref="InputDevice.TryGetFeatureValue"/> to get eye tracking related input features. See <see cref="CommonUsages"/> for additional usages.
|
||||
/// </summary>
|
||||
public static class EyeTrackingUsages
|
||||
{
|
||||
/// <summary>The origin position for the gaze. The gaze represents where a user is looking, and <see cref="gazePosition"/> represents the starting location, close to the eyes, from which to project a gaze ray from.</summary>
|
||||
public static InputFeatureUsage<Vector3> gazePosition = new InputFeatureUsage<Vector3>("gazePosition");
|
||||
/// <summary>The orientation of the gaze, such that the direction of the gaze is the same as <see cref="Vector3.forward "/> * gazeRotation. Use with <see cref="gazePosition"/> to create a gaze ray.</summary>
|
||||
public static InputFeatureUsage<Quaternion> gazeRotation = new InputFeatureUsage<Quaternion>("gazeRotation");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3cf79659a011bd419c7a2a30eb74e9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,558 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
#if USE_STICK_CONTROL_THUMBSTICKS
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.StickControl; // If replaced, make sure the control extends Vector2Control
|
||||
#else
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.Vector2Control;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of HP Reverb G2 Controller interaction profiles in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "HP Reverb G2 Controller Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA},
|
||||
Company = "Unity",
|
||||
Desc = "Allows for mapping input to the HP Reverb G2 Controller interaction profile.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/hpreverbg2controllerprofile.html",
|
||||
OpenxrExtensionStrings = "XR_EXT_hp_mixed_reality_controller",
|
||||
Version = "0.0.1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class HPReverbG2ControllerProfile : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.hpreverb";
|
||||
|
||||
/// <summary>
|
||||
/// An Input System device based off the <a href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_hp_mixed_reality_controller">HP Reverb G2 Controller</a>.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "HP Reverb G2 Controller (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class ReverbG2Controller : XRControllerWithRumble
|
||||
{
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HPReverbG2ControllerProfile.buttonA"/> <see cref="HPReverbG2ControllerProfile.buttonX"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "A", "X", "buttonA", "buttonX" }, usage = "PrimaryButton")]
|
||||
public ButtonControl primaryButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HPReverbG2ControllerProfile.buttonB"/> <see cref="HPReverbG2ControllerProfile.buttonY"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "B", "Y", "buttonB", "buttonY" }, usage = "SecondaryButton")]
|
||||
public ButtonControl secondaryButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents information from the <see cref="HPReverbG2ControllerProfile.menu"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary", "menubutton" }, usage = "MenuButton")]
|
||||
public ButtonControl menu { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="HPReverbG2ControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripAxis", "squeeze" }, usage = "Grip")]
|
||||
public AxisControl grip { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HPReverbG2ControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripButton", "squeezeClicked" }, usage = "GripButton")]
|
||||
public ButtonControl gripPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="HPReverbG2ControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Trigger")]
|
||||
public AxisControl trigger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HPReverbG2ControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "indexButton", "indexTouched", "triggerbutton" }, usage = "TriggerButton")]
|
||||
public ButtonControl triggerPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control)/[StickControl](xref:UnityEngine.InputSystem.Controls.StickControl) that represents the <see cref="HPReverbG2ControllerProfile.thumbstick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary2DAxis", "Joystick" }, usage = "Primary2DAxis")]
|
||||
public ThumbstickControl thumbstick { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HPReverbG2ControllerProfile.thumbstickClick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "JoystickOrPadPressed", "thumbstickClick", "joystickClicked" }, usage = "Primary2DAxisClick")]
|
||||
public ButtonControl thumbstickClicked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="HPReverbG2ControllerProfile.grip"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents information from the <see cref="HPReverbG2ControllerProfile.aim"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 29)]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for back compatibility with the XRSDK layouts. This represents the bit flag set indicating what data is valid. This value is equivalent to mapping devicePose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 32)]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for back compatibility with the XRSDK layouts. This is the device position. This is both the grip and the pointer position. This value is equivalent to mapping devicePose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 36, alias = "gripPosition")]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation. This is both the grip and the pointer rotation. This value is equivalent to mapping devicePose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 48, alias = "gripOrientation")]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for back compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 96)]
|
||||
public Vector3Control pointerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 108, alias = "pointerOrientation")]
|
||||
public QuaternionControl pointerRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="HapticControl"/> that represents the <see cref="HPReverbG2ControllerProfile.haptic"/> binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Haptic")]
|
||||
public HapticControl haptic { get; private set; }
|
||||
|
||||
/// <inheritdoc cref="OpenXRDevice"/>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
primaryButton = GetChildControl<ButtonControl>("primaryButton");
|
||||
secondaryButton = GetChildControl<ButtonControl>("secondaryButton");
|
||||
|
||||
menu = GetChildControl<ButtonControl>("menu");
|
||||
grip = GetChildControl<AxisControl>("grip");
|
||||
gripPressed = GetChildControl<ButtonControl>("gripPressed");
|
||||
trigger = GetChildControl<AxisControl>("trigger");
|
||||
triggerPressed = GetChildControl<ButtonControl>("triggerPressed");
|
||||
thumbstick = GetChildControl<StickControl>("thumbstick");
|
||||
thumbstickClicked = GetChildControl<ButtonControl>("thumbstickClicked");
|
||||
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
|
||||
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
|
||||
|
||||
haptic = GetChildControl<HapticControl>("haptic");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The interaction profile string used to reference the <a href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_hp_mixed_reality_controller">HP Reverb G2 Controller</a>.</summary>
|
||||
public const string profile = "/interaction_profiles/hp/mixed_reality_controller";
|
||||
|
||||
// Available Bindings
|
||||
// Left Hand Only
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/x/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonX = "/input/x/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/y/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonY = "/input/y/click";
|
||||
|
||||
// Right Hand Only
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/a/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonA = "/input/a/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '..."/input/b/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonB = "/input/b/click";
|
||||
|
||||
// Both Hands
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/menu/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string menu = "/input/menu/click";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/squeeze/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string squeeze = "/input/squeeze/value";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trigger = "/input/trigger/value";
|
||||
/// <summary>
|
||||
/// Constant for a Vector2 interaction binding '.../input/thumbstick' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstick = "/input/thumbstick";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumbstick/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstickClick = "/input/thumbstick/click";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string haptic = "/output/haptic";
|
||||
|
||||
private const string kDeviceLocalizedName = "HP Reverb G2 Controller OpenXR";
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override bool OnInstanceCreate(ulong instance)
|
||||
{
|
||||
// Requires extension to enable
|
||||
if (!OpenXRRuntime.IsExtensionEnabled("XR_EXT_hp_mixed_reality_controller"))
|
||||
return false;
|
||||
|
||||
return base.OnInstanceCreate(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="ReverbG2Controller"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(ReverbG2Controller),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="ReverbG2Controller"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(ReverbG2Controller));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device for Input System.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(ReverbG2Controller);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "hpreverbg2controller",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "HP",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
//A / X Press
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "primaryButton",
|
||||
localizedName = "Primary Button",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonX,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonA,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//B / Y Press
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "secondaryButton",
|
||||
localizedName = "Secondary Button",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"SecondaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonY,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonB,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "menu",
|
||||
localizedName = "Menu",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"MenuButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = menu,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "grip",
|
||||
localizedName = "Grip",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Grip"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "gripPressed",
|
||||
localizedName = "Grip Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GripButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trigger",
|
||||
localizedName = "Trigger",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Trigger"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerPressed",
|
||||
localizedName = "Trigger Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstick",
|
||||
localizedName = "Thumbstick",
|
||||
type = ActionType.Axis2D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxis"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickClicked",
|
||||
localizedName = "Thumbstick Clicked",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisClick"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickClick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Device Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Haptics
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "haptic",
|
||||
localizedName = "Haptic Output",
|
||||
type = ActionType.Vibrate,
|
||||
usages = new List<string>() { "Haptic" },
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = haptic,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5315f812f023cf4ebf26f7e5d2d70f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,521 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
#if USE_STICK_CONTROL_THUMBSTICKS
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.StickControl; // If replaced, make sure the control extends Vector2Control
|
||||
#else
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.Vector2Control;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of HTC Vive Controllers interaction profiles in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "HTC Vive Controller Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA},
|
||||
Company = "Unity",
|
||||
Desc = "Allows for mapping input to the HTC Vive Controller interaction profile.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/htcvivecontrollerprofile.html",
|
||||
OpenxrExtensionStrings = "",
|
||||
Version = "0.0.1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class HTCViveControllerProfile : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.htcvive";
|
||||
|
||||
/// <summary>
|
||||
/// An Input System device based off the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#_htc_vive_controller_profile">HTC Vive Controller</a>.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "HTC Vive Controller (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class ViveController : XRControllerWithRumble
|
||||
{
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents information from the HTC Vive Controller Profile select OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Secondary", "selectbutton" }, usage = "SystemButton")]
|
||||
public ButtonControl select { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents information from the <see cref="HTCViveControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripAxis", "squeeze" }, usage = "Grip")]
|
||||
public AxisControl grip { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents information from the <see cref="HTCViveControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripButton", "squeezeClicked" }, usage = "GripButton")]
|
||||
public ButtonControl gripPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents information from the <see cref="HTCViveControllerProfile.menu"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary", "menubutton" }, usage = "MenuButton")]
|
||||
public ButtonControl menu { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents information from the <see cref="HTCViveControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(alias = "triggeraxis", usage = "Trigger")]
|
||||
public AxisControl trigger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents information from the <see cref="HTCViveControllerProfile.triggerClick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(alias = "triggerbutton", usage = "TriggerButton")]
|
||||
public ButtonControl triggerPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control)/[StickControl](xref:UnityEngine.InputSystem.Controls.StickControl) that represents information from the <see cref="HTCViveControllerProfile.trackpad"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary2DAxis", "touchpadaxes", "touchpad" }, usage = "Primary2DAxis")]
|
||||
public ThumbstickControl trackpad { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents information from the <see cref="HTCViveControllerProfile.trackpadClick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "joystickorpadpressed", "touchpadpressed" }, usage = "Primary2DAxisClick")]
|
||||
public ButtonControl trackpadClicked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents information from the <see cref="HTCViveControllerProfile.trackpadTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "joystickorpadtouched", "touchpadtouched" }, usage = "Primary2DAxisTouch")]
|
||||
public ButtonControl trackpadTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents information from the <see cref="HTCViveControllerProfile.grip"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents information from the <see cref="HTCViveControllerProfile.aim"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 26)]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for back compatibility with the XRSDK layouts. This represents the bit flag set indicating what data is valid. This value is equivalent to mapping devicePose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 28)]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for back compatibility with the XRSDK layouts. This is the device position. For the Oculus Touch device, this is both the grip and the pointer position. This value is equivalent to mapping devicePose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 32, alias = "gripPosition")]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation. For the Oculus Touch device, this is both the grip and the pointer rotation. This value is equivalent to mapping devicePose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 44, alias = "gripOrientation")]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for back compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 92)]
|
||||
public Vector3Control pointerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 104, alias = "pointerOrientation")]
|
||||
public QuaternionControl pointerRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="HapticControl"/> that represents the <see cref="HTCViveControllerProfile.haptic"/> binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Haptic")]
|
||||
public HapticControl haptic { get; private set; }
|
||||
|
||||
/// <inheritdoc cref="OpenXRDevice"/>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
select = GetChildControl<ButtonControl>("select");
|
||||
grip = GetChildControl<AxisControl>("grip");
|
||||
gripPressed = GetChildControl<ButtonControl>("gripPressed");
|
||||
menu = GetChildControl<ButtonControl>("menu");
|
||||
trigger = GetChildControl<AxisControl>("trigger");
|
||||
triggerPressed = GetChildControl<ButtonControl>("triggerPressed");
|
||||
trackpad = GetChildControl<StickControl>("trackpad");
|
||||
trackpadClicked = GetChildControl<ButtonControl>("trackpadClicked");
|
||||
trackpadTouched = GetChildControl<ButtonControl>("trackpadTouched");
|
||||
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
|
||||
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
|
||||
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
|
||||
haptic = GetChildControl<HapticControl>("haptic");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The interaction profile string used to reference the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#_htc_vive_controller_profile">HTC Vive Controller</a>.</summary>
|
||||
public const string profile = "/interaction_profiles/htc/vive_controller";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/system/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string system = "/input/system/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/squeeze/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string squeeze = "/input/squeeze/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/menu/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string menu = "/input/menu/click";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trigger = "/input/trigger/value";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trigger/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerClick = "/input/trigger/click";
|
||||
/// <summary>
|
||||
/// Constant for a Vector2 interaction binding '.../input/trackpad' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trackpad = "/input/trackpad";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trackpad/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trackpadClick = "/input/trackpad/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trackpad/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trackpadTouch = "/input/trackpad/touch";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string haptic = "/output/haptic";
|
||||
|
||||
private const string kDeviceLocalizedName = "HTC Vive Controller OpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="ViveController"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(ViveController),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="ViveController"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(ViveController));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device for the Input System.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(ViveController);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "htcvivecontroller",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "HTC",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "grip",
|
||||
localizedName = "Grip",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Grip"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "gripPressed",
|
||||
localizedName = "Grip Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GripButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "menu",
|
||||
localizedName = "Menu",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"MenuButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = menu,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "select",
|
||||
localizedName = "Select",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"SystemButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = system,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trigger",
|
||||
localizedName = "Trigger",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Trigger"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerPressed",
|
||||
localizedName = "Trigger Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerClick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpad",
|
||||
localizedName = "Trackpad",
|
||||
type = ActionType.Axis2D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxis"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpad,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpadTouched",
|
||||
localizedName = "Trackpad Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpadClicked",
|
||||
localizedName = "Trackpad Clicked",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisClick"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadClick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Device Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Haptics
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "haptic",
|
||||
localizedName = "Haptic Output",
|
||||
type = ActionType.Vibrate,
|
||||
usages = new List<string>() { "Haptic" },
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = haptic,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 274c02963f889a64e90bc2e596e21d13
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,339 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of hand common poses profiles in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Hand Interaction Poses",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
|
||||
Company = "Unity",
|
||||
Desc = "Add hand common interaction poses feature, if enabled, four additional commonly used poses will be supported.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/handcommonposesinteraction.html",
|
||||
OpenxrExtensionStrings = extensionString,
|
||||
Version = "0.0.1",
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class HandCommonPosesInteraction : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.handinteractionposes";
|
||||
|
||||
/// <summary>
|
||||
/// A flag to mark this hand interaction feature is potentially additive.
|
||||
/// </summary>
|
||||
internal override bool IsAdditive => true;
|
||||
|
||||
/// <summary>
|
||||
/// An interaction feature that supports commonly used hand poses for hand interactions across motion controller and hand tracking devices.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Hand Interaction Poses (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" }, isGenericTypeOfDevice = true)]
|
||||
public class HandInteractionPoses : OpenXRDevice
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="HandCommonPosesInteraction.grip"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="HandCommonPosesInteraction.aim"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="HandCommonPosesInteraction.poke"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0)]
|
||||
public PoseControl pokePose { get; private set; }
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="HandCommonPosesInteraction.pinch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0)]
|
||||
public PoseControl pinchPose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal call used to assign controls to the the correct element.
|
||||
/// </summary>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
pokePose = GetChildControl<PoseControl>("pokePose");
|
||||
pinchPose = GetChildControl<PoseControl>("pinchPose");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The interaction profile string used to reference Hand Common Poses feature.
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/unity/hand_interaction_poses";
|
||||
|
||||
// Available Bindings
|
||||
// Both Hands
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/poke_ext/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string poke = "/input/poke_ext/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/pinch_ext/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string pinch = "/input/pinch_ext/pose";
|
||||
|
||||
private const string kDeviceLocalizedName = "Hand Interaction Poses OpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// The OpenXR Extension string. This is used by OpenXR to check if this extension is available or enabled.
|
||||
/// </summary>
|
||||
public const string extensionString = "XR_EXT_hand_interaction";
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected internal override void GetValidationChecks(List<OpenXRFeature.ValidationRule> results, BuildTargetGroup target)
|
||||
{
|
||||
results.Add( new ValidationRule(this){
|
||||
message = "Additive Interaction feature requires a valid controller or hand interaction profile selected within Interaction Profiles.",
|
||||
error = true,
|
||||
errorEnteringPlaymode = true,
|
||||
checkPredicate = () =>
|
||||
{
|
||||
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(target);
|
||||
if (null == settings)
|
||||
return false;
|
||||
|
||||
bool handCommonPosesFeatureEnabled = false;
|
||||
bool otherNonAdditiveInteractionFeatureEnabled = false;
|
||||
foreach (var feature in settings.GetFeatures<OpenXRInteractionFeature>())
|
||||
{
|
||||
if (feature.enabled)
|
||||
{
|
||||
if (feature is HandCommonPosesInteraction)
|
||||
handCommonPosesFeatureEnabled = true;
|
||||
else if (!(feature as OpenXRInteractionFeature).IsAdditive && !(feature is EyeGazeInteraction))
|
||||
otherNonAdditiveInteractionFeatureEnabled = true;
|
||||
}
|
||||
}
|
||||
return handCommonPosesFeatureEnabled && otherNonAdditiveInteractionFeatureEnabled;
|
||||
},
|
||||
fixIt = () => SettingsService.OpenProjectSettings("Project/XR Plug-in Management/OpenXR"),
|
||||
fixItAutomatic = false,
|
||||
fixItMessage = "Open Project Settings to select one or more non Additive interaction profiles."
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override bool OnInstanceCreate(ulong instance)
|
||||
{
|
||||
// Requires hand tracking extension
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(extensionString))
|
||||
return false;
|
||||
|
||||
return base.OnInstanceCreate(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="HandInteractionPoses"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(HandInteractionPoses),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="HandInteractionPoses"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(HandInteractionPoses));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return Interaction profile type. Hand common poses profile is Device type.
|
||||
/// </summary>
|
||||
/// <returns>Interaction profile type.</returns>
|
||||
protected override InteractionProfileType GetInteractionProfileType()
|
||||
{
|
||||
return typeof(HandInteractionPoses).IsSubclassOf(typeof(XRController)) ? InteractionProfileType.XRController : InteractionProfileType.Device;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used to register device in InputSystem.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(HandInteractionPoses);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "handinteractionposes",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
// Device Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Device Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
//Poke Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "PokePose",
|
||||
localizedName = "Poke Pose",
|
||||
type = ActionType.Pose,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = poke,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
},
|
||||
//Pinch Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "PinchPose",
|
||||
localizedName = "Pinch Pose",
|
||||
type = ActionType.Pose,
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = pinch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
},
|
||||
isAdditive = true
|
||||
}
|
||||
}
|
||||
};
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
|
||||
//Process additive actions: add additional supported additive actions to existing controller or hand interaction profiles
|
||||
internal override void AddAdditiveActions(List<OpenXRInteractionFeature.ActionMapConfig> actionMaps, ActionMapConfig additiveMap)
|
||||
{
|
||||
foreach (var actionMap in actionMaps)
|
||||
{
|
||||
//valid userPath is user/hand/left or user/hand/right
|
||||
var validUserPath = actionMap.deviceInfos.Where(d => d.userPath != null && ((String.CompareOrdinal(d.userPath, OpenXRInteractionFeature.UserPaths.leftHand) == 0) ||
|
||||
(String.CompareOrdinal(d.userPath, OpenXRInteractionFeature.UserPaths.rightHand) == 0)));
|
||||
|
||||
if (validUserPath.Any())
|
||||
{
|
||||
foreach (var additiveAction in additiveMap.actions.Where(a => a.isAdditive))
|
||||
{
|
||||
bool duplicateFound = false;
|
||||
var poseActions = actionMap.actions.Where(m => m.type == ActionType.Pose).Distinct().ToList();
|
||||
foreach (var poseAction in poseActions)
|
||||
{
|
||||
if ((poseAction.bindings.Where(b => b.interactionPath != null && (String.CompareOrdinal(b.interactionPath, additiveAction.bindings[0].interactionPath) == 0))).Any())
|
||||
{
|
||||
poseAction.isAdditive = true;
|
||||
duplicateFound = true;
|
||||
}
|
||||
}
|
||||
if (!duplicateFound)
|
||||
actionMap.actions.Add(additiveAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a24be4b5ebfe5f4d8ed1de9b25cb7aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,576 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of New Hand interaction profiles in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Hand Interaction Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
|
||||
Company = "Unity",
|
||||
Desc = "Add hand interaction profile for hand tracking input device.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/handinteractionprofile.html",
|
||||
OpenxrExtensionStrings = extensionString,
|
||||
Version = "0.0.1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class HandInteractionProfile : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.handinteraction";
|
||||
|
||||
/// <summary>
|
||||
/// A new interaction profile for hand tracking input device to provide actions through the OpenXR action system.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Hand Interaction (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class HandInteraction : XRController
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="HandInteractionProfile.grip"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="HandInteractionProfile.aim"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="HandInteractionProfile.poke"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, usage = "Poke")]
|
||||
public PoseControl pokePose { get; private set; }
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="HandInteractionProfile.pinch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, usage = "Pinch")]
|
||||
public PoseControl pinchPose { get; private set; }
|
||||
/// <summary>
|
||||
/// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="HandInteractionProfile.pinchValue"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "PinchValue")]
|
||||
public AxisControl pinchValue { get; private set; }
|
||||
/// <summary>
|
||||
/// An [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HandInteractionProfile.pinchValue"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "PinchTouched")]
|
||||
public ButtonControl pinchTouched { get; private set; }
|
||||
/// <summary>
|
||||
/// An [AxisControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HandInteractionProfile.pinchReady"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "PinchReady")]
|
||||
public ButtonControl pinchReady { get; private set; }
|
||||
/// <summary>
|
||||
/// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="HandInteractionProfile.pointerActivateValue"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "PointerActivateValue")]
|
||||
public AxisControl pointerActivateValue { get; private set; }
|
||||
/// <summary>
|
||||
/// An [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HandInteractionProfile.pointerActivateValue"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "PointerActivated")]
|
||||
public ButtonControl pointerActivated { get; private set; }
|
||||
/// <summary>
|
||||
/// An [AxisControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HandInteractionProfile.pointerActivateReady"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "PointerActivateReady")]
|
||||
public ButtonControl pointerActivateReady { get; private set; }
|
||||
/// <summary>
|
||||
/// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="HandInteractionProfile.graspValue"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "GraspValue")]
|
||||
public AxisControl graspValue { get; private set; }
|
||||
/// <summary>
|
||||
/// An [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HandInteractionProfile.graspValue"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "GraspFirm")]
|
||||
public ButtonControl graspFirm { get; private set; }
|
||||
/// <summary>
|
||||
/// An [AxisControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="HandInteractionProfile.graspReady"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "GraspReady")]
|
||||
public ButtonControl graspReady { get; private set; }
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping gripPose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 2)]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set to indicate what data is valid. This value is equivalent to mapping gripPose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 4)]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position. This value is equivalent to mapping gripPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 8, noisy = true, alias = "gripPosition")]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation. This value is equivalent to mapping gripPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 20, noisy = true, alias = "gripRotation")]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the aim position. This value is equivalent to mapping aimPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 68, noisy = true)]
|
||||
public Vector3Control pointerPosition { get; private set; }
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the aim orientation. This value is equivalent to mapping aimPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 80, noisy = true)]
|
||||
public QuaternionControl pointerRotation { get; private set; }
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the poke position. This value is equivalent to mapping pokePose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 128, noisy = true)]
|
||||
public Vector3Control pokePosition { get; private set; }
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the poke orientation. This value is equivalent to mapping pokePose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 140, noisy = true)]
|
||||
public QuaternionControl pokeRotation { get; private set; }
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the pinch position. This value is equivalent to mapping pinchPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 188, noisy = true)]
|
||||
public Vector3Control pinchPosition { get; private set; }
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pinch orientation. This value is equivalent to mapping pinchPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 200, noisy = true)]
|
||||
public QuaternionControl pinchRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal call used to assign controls to the the correct element.
|
||||
/// </summary>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
pokePose = GetChildControl<PoseControl>("pokePose");
|
||||
pinchPose = GetChildControl<PoseControl>("pinchPose");
|
||||
pinchValue = GetChildControl<AxisControl>("pinchValue");
|
||||
pinchTouched = GetChildControl<ButtonControl>("pinchTouched");
|
||||
pinchReady = GetChildControl<ButtonControl>("pinchReady");
|
||||
pointerActivateValue = GetChildControl<AxisControl>("pointerActivateValue");
|
||||
pointerActivated = GetChildControl<ButtonControl>("pointerActivated");
|
||||
pointerActivateReady = GetChildControl<ButtonControl>("pointerActivateReady");
|
||||
graspValue = GetChildControl<AxisControl>("graspValue");
|
||||
graspFirm = GetChildControl<ButtonControl>("graspFirm");
|
||||
graspReady = GetChildControl<ButtonControl>("graspReady");
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
|
||||
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
|
||||
pokePosition = GetChildControl<Vector3Control>("pokePosition");
|
||||
pokeRotation = GetChildControl<QuaternionControl>("pokeRotation");
|
||||
pinchPosition = GetChildControl<Vector3Control>("pinchPosition");
|
||||
pinchRotation = GetChildControl<QuaternionControl>("pinchRotation");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The interaction profile string used to reference Hand Interaction Profile.
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/ext/hand_interaction_ext";
|
||||
|
||||
// Available Bindings
|
||||
// Both Hands
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/poke_ext/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string poke = "/input/poke_ext/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/pinch_ext/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string pinch = "/input/pinch_ext/pose";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/pinch_ext/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string pinchValue = "/input/pinch_ext/value";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/pinch_ext/ready_ext' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string pinchReady = "/input/pinch_ext/ready_ext";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/aim_activate_ext/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string pointerActivateValue = "/input/aim_activate_ext/value";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/aim_activate_ext/ready_ext' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string pointerActivateReady = "/input/aim_activate_ext/ready_ext";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/grasp_ext/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string graspValue = "/input/grasp_ext/value";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/grasp_ext/ready_ext' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string graspReady = "/input/grasp_ext/ready_ext";
|
||||
|
||||
private const string kDeviceLocalizedName = "Hand Interaction OpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// The OpenXR Extension string. This is used by OpenXR to check if this extension is available or enabled.
|
||||
/// </summary>
|
||||
public const string extensionString = "XR_EXT_hand_interaction";
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override bool OnInstanceCreate(ulong instance)
|
||||
{
|
||||
// Requires hand tracking extension
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(extensionString))
|
||||
return false;
|
||||
|
||||
return base.OnInstanceCreate(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="HandInteraction"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(HandInteraction),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="HandInteraction"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(HandInteraction));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device in InputSystem.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(HandInteraction);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "handinteraction",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
// Device Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Grip Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Poke Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "PokePose",
|
||||
localizedName = "Poke Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Poke"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = poke,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Pinch Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "PinchPose",
|
||||
localizedName = "Pinch Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pinch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = pinch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Pinch Value
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "PinchValue",
|
||||
localizedName = "Pinch Value",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PinchValue"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = pinchValue,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Pinch Touched
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "PinchTouched",
|
||||
localizedName = "Pinch Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PinchTouched"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = pinchValue,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Pinch Ready
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "PinchReady",
|
||||
localizedName = "Pinch Ready",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PinchReady"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = pinchReady,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Pointer Activate Value
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "PointerActivateValue",
|
||||
localizedName = "Pointer Activate Value",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PointerActivateValue"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = pointerActivateValue,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Pointer Activated
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "PointerActivated",
|
||||
localizedName = "Pointer Activated",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PointerActivated"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = pointerActivateValue,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Pointer Activate Ready
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "PointerActivateReady",
|
||||
localizedName = "Pointer Activate Ready",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PointerActivateReady"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = pointerActivateReady,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Grasp Value
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "GraspValue",
|
||||
localizedName = "Grasp Value",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GraspValue"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = graspValue,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Grasp Firm
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "GraspFirm",
|
||||
localizedName = "Grasp Firm",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GraspFirm"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = graspValue,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Grasp Ready
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "GraspReady",
|
||||
localizedName = "Grasp Ready",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GraspReady"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = graspReady,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5019471fb2174e5c852ecd4047163007
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,324 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of KHR Simple Controllers interaction profiles in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Khronos Simple Controller Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
|
||||
Company = "Unity",
|
||||
Desc = "Allows for mapping input to the Khronos Simple Controller interaction profile.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/khrsimplecontrollerprofile.html",
|
||||
OpenxrExtensionStrings = "",
|
||||
Version = "0.0.1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class KHRSimpleControllerProfile : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.khrsimpleprofile";
|
||||
|
||||
/// <summary>
|
||||
/// An Input System device based off the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#_khronos_simple_controller_profile">Khronos Simple Controller interaction profile</a>. This device contains one haptic output motor.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Khronos Simple Controller (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class KHRSimpleController : XRControllerWithRumble
|
||||
{
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="KHRSimpleControllerProfile.select"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Secondary", "selectbutton" }, usage = "PrimaryButton")]
|
||||
public ButtonControl select { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="KHRSimpleControllerProfile.menu"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary", "menubutton" }, usage = "MenuButton")]
|
||||
public ButtonControl menu { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents information from the <see cref="KHRSimpleControllerProfile.grip"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents information from the <see cref="KHRSimpleControllerProfile.aim"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 2)]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set indicating what data is valid. This value is equivalent to mapping devicePose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 4)]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position, or grip position. This value is equivalent to mapping devicePose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 8, alias = "gripPosition")]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation, or grip orientation. This value is equivalent to mapping devicePose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 20, alias = "gripOrientation")]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 68)]
|
||||
public Vector3Control pointerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 80, alias = "pointerOrientation")]
|
||||
public QuaternionControl pointerRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="HapticControl"/> that represents the <see cref="KHRSimpleControllerProfile.haptic"/> binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Haptic")]
|
||||
public HapticControl haptic { get; private set; }
|
||||
|
||||
/// <inheritdoc cref="OpenXRDevice"/>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
menu = GetChildControl<ButtonControl>("menu");
|
||||
select = GetChildControl<ButtonControl>("select");
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
|
||||
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
|
||||
|
||||
haptic = GetChildControl<HapticControl>("haptic");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OpenXR string that represents the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-interaction-profiles">Interaction Profile.</a>
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/khr/simple_controller";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/select/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string select = "/input/select/click";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/menu/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string menu = "/input/menu/click";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../input/output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string haptic = "/output/haptic";
|
||||
|
||||
private const string kDeviceLocalizedName = "KHR Simple Controller OpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="KHRSimpleController"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(KHRSimpleController),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="KHRSimpleController"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(typeof(KHRSimpleController).Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device for the Input System.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(KHRSimpleController);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "khrsimplecontroller",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "Khronos",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
// Menu
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "select",
|
||||
localizedName = "Select",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = select,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Trigger Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "menu",
|
||||
localizedName = "Menu",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"MenuButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = menu,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Device Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Device Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Haptics
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "haptic",
|
||||
localizedName = "Haptic Output",
|
||||
type = ActionType.Vibrate,
|
||||
usages = new List<string>() { "Haptic" },
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = haptic,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f6bfdbcb316ed242b30a8798c9eb853
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,902 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
#if USE_STICK_CONTROL_THUMBSTICKS
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.StickControl; // If replaced, make sure the control extends Vector2Control
|
||||
#else
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.Vector2Control;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of Meta Quest Touch Plus controller interaction profiles in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Meta Quest Touch Plus Controller Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
|
||||
Company = "Unity",
|
||||
Desc = "Allows for mapping input to the Meta Quest Touch Plus Controller interaction profile.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/metaquesttouchpluscontrollerprofile.html",
|
||||
OpenxrExtensionStrings = "XR_META_touch_controller_plus",
|
||||
Version = "0.0.1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class MetaQuestTouchPlusControllerProfile : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.metaquestplus";
|
||||
|
||||
/// <summary>
|
||||
/// An Input System device based on the controller interaction profile Meta Touch Controller Plus.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Meta Quest Touch Plus Controller(OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class QuestTouchPlusController : XRControllerWithRumble
|
||||
{
|
||||
/// <summary>
|
||||
/// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control)/[StickControl](xref:UnityEngine.InputSystem.Controls.StickControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.thumbstick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary2DAxis", "Joystick" }, usage = "Primary2DAxis")]
|
||||
public ThumbstickControl thumbstick { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripAxis", "squeeze" }, usage = "Grip")]
|
||||
public AxisControl grip { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripButton", "squeezeClicked" }, usage = "GripButton")]
|
||||
public ButtonControl gripPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.system"/> <see cref="MetaQuestTouchPlusControllerProfile.menu"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary", "menuButton", "systemButton" }, usage = "MenuButton")]
|
||||
public ButtonControl menu { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.buttonA"/> <see cref="MetaQuestTouchPlusControllerProfile.buttonX"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "A", "X", "buttonA", "buttonX" }, usage = "PrimaryButton")]
|
||||
public ButtonControl primaryButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.buttonATouch"/> <see cref="MetaQuestTouchPlusControllerProfile.buttonYTouch"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "ATouched", "XTouched", "ATouch", "XTouch", "buttonATouched", "buttonXTouched" }, usage = "PrimaryTouch")]
|
||||
public ButtonControl primaryTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.buttonB"/> <see cref="MetaQuestTouchPlusControllerProfile.buttonY"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "B", "Y", "buttonB", "buttonY" }, usage = "SecondaryButton")]
|
||||
public ButtonControl secondaryButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.buttonBTouch"/> <see cref="MetaQuestTouchPlusControllerProfile.buttonYTouch"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "BTouched", "YTouched", "BTouch", "YTouch", "buttonBTouched", "buttonYTouched" }, usage = "SecondaryTouch")]
|
||||
public ButtonControl secondaryTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Trigger")]
|
||||
public AxisControl trigger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "indexButton", "indexTouched", "triggerbutton" }, usage = "TriggerButton")]
|
||||
public ButtonControl triggerPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.triggerTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "indexTouch", "indexNearTouched" }, usage = "TriggerTouch")]
|
||||
public ButtonControl triggerTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.thumbstickClick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "JoystickOrPadPressed", "thumbstickClick", "joystickClicked" }, usage = "Primary2DAxisClick")]
|
||||
public ButtonControl thumbstickClicked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.thumbstickTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "JoystickOrPadTouched", "thumbstickTouch", "joystickTouched" }, usage = "Primary2DAxisTouch")]
|
||||
public ButtonControl thumbstickTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.thumbrest"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "ThumbrestTouch")]
|
||||
public ButtonControl thumbrestTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="MetaQuestTouchPlusControllerProfile.grip"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="MetaQuestTouchPlusControllerProfile.aim"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 28, usage = "IsTracked")]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set to indicate what data is valid. This value is equivalent to mapping devicePose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 32, usage = "TrackingState")]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position. For the Oculus Touch device, this is both the grip and the pointer position. This value is equivalent to mapping devicePose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 36, noisy = true, alias = "gripPosition")]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation. For the Oculus Touch device, this is both the grip and the pointer rotation. This value is equivalent to mapping devicePose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 48, noisy = true, alias = "gripOrientation")]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for back compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 96)]
|
||||
public Vector3Control pointerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 108, alias = "pointerOrientation")]
|
||||
public QuaternionControl pointerRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="HapticControl"/> that represents the <see cref="MetaQuestTouchPlusControllerProfile.haptic"/> binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Haptic")]
|
||||
public HapticControl haptic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.triggerForce"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "TriggerForce")]
|
||||
public AxisControl triggerForce { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.triggerCurl"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "TriggerCurl")]
|
||||
public AxisControl triggerCurl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.triggerSlide"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "TriggerSlide")]
|
||||
public AxisControl triggerSlide { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.triggerProximity"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "TriggerProximity")]
|
||||
public ButtonControl triggerProximity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchPlusControllerProfile.thumbProximity"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "ThumbProximity")]
|
||||
public ButtonControl thumbProximity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal call used to assign controls to the the correct element.
|
||||
/// </summary>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
|
||||
thumbstick = GetChildControl<StickControl>("thumbstick");
|
||||
trigger = GetChildControl<AxisControl>("trigger");
|
||||
triggerPressed = GetChildControl<ButtonControl>("triggerPressed");
|
||||
triggerTouched = GetChildControl<ButtonControl>("triggerTouched");
|
||||
grip = GetChildControl<AxisControl>("grip");
|
||||
gripPressed = GetChildControl<ButtonControl>("gripPressed");
|
||||
menu = GetChildControl<ButtonControl>("menu");
|
||||
primaryButton = GetChildControl<ButtonControl>("primaryButton");
|
||||
primaryTouched = GetChildControl<ButtonControl>("primaryTouched");
|
||||
secondaryButton = GetChildControl<ButtonControl>("secondaryButton");
|
||||
secondaryTouched = GetChildControl<ButtonControl>("secondaryTouched");
|
||||
thumbstickClicked = GetChildControl<ButtonControl>("thumbstickClicked");
|
||||
thumbstickTouched = GetChildControl<ButtonControl>("thumbstickTouched");
|
||||
thumbrestTouched = GetChildControl<ButtonControl>("thumbrestTouched");
|
||||
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
|
||||
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
|
||||
|
||||
haptic = GetChildControl<HapticControl>("haptic");
|
||||
triggerForce = GetChildControl<AxisControl>("triggerForce");
|
||||
triggerCurl = GetChildControl<AxisControl>("triggerCurl");
|
||||
triggerSlide = GetChildControl<AxisControl>("triggerSlide");
|
||||
triggerProximity = GetChildControl<ButtonControl>("triggerProximity");
|
||||
thumbProximity = GetChildControl<ButtonControl>("thumbProximity");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The interaction profile string used to reference Meta Quest Touch Plus Controller.
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/meta/touch_controller_plus";
|
||||
|
||||
// Available Bindings
|
||||
// Left Hand Only
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/x/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonX = "/input/x/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/x/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonXTouch = "/input/x/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/y/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonY = "/input/y/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/y/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonYTouch = "/input/y/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/menu/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string menu = "/input/menu/click";
|
||||
|
||||
// Right Hand Only
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/a/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonA = "/input/a/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/a/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonATouch = "/input/a/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '..."/input/b/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonB = "/input/b/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/b/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonBTouch = "/input/b/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/system/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string system = "/input/system/click";
|
||||
|
||||
// Both Hands
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/squeeze/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string squeeze = "/input/squeeze/value";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trigger = "/input/trigger/value";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trigger/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerTouch = "/input/trigger/touch";
|
||||
/// <summary>
|
||||
/// Constant for a Vector2 interaction binding '...//input/thumbstick' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstick = "/input/thumbstick";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumbstick/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstickClick = "/input/thumbstick/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumbstick/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstickTouch = "/input/thumbstick/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumbrest/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbrest = "/input/thumbrest/touch";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string haptic = "/output/haptic";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/force' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerForce = "/input/trigger/force";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/curl_meta' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerCurl = "/input/trigger/curl_meta";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/slide_meta' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerSlide = "/input/trigger/slide_meta";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trigger/proximity_meta' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerProximity = "/input/trigger/proximity_meta";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumb_meta/proximity_meta' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbProximity = "/input/thumb_meta/proximity_meta";
|
||||
|
||||
private const string kDeviceLocalizedName = "Meta Quest Touch Plus Controller OpenXR";
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override bool OnInstanceCreate(ulong instance)
|
||||
{
|
||||
// Requires the plus controller extension
|
||||
if (!OpenXRRuntime.IsExtensionEnabled("XR_META_touch_controller_plus"))
|
||||
return false;
|
||||
|
||||
return base.OnInstanceCreate(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="QuestTouchPlusController"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(QuestTouchPlusController),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="QuestTouchPlusController"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(QuestTouchPlusController));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device for the Input System.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(QuestTouchPlusController);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "questtouchpluscontroller",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "Oculus",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
// Joystick
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstick",
|
||||
localizedName = "Thumbstick",
|
||||
type = ActionType.Axis2D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxis"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Grip
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "grip",
|
||||
localizedName = "Grip",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Grip"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Grip Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "gripPressed",
|
||||
localizedName = "Grip Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GripButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Menu
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "menu",
|
||||
localizedName = "Menu",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"MenuButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = menu,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = system,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//A / X Press
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "primaryButton",
|
||||
localizedName = "Primary Button",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonX,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonA,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//A / X Touch
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "primaryTouched",
|
||||
localizedName = "Primary Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonXTouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonATouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//B / Y Press
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "secondaryButton",
|
||||
localizedName = "Secondary Button",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"SecondaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonY,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonB,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//B / Y Touch
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "secondaryTouched",
|
||||
localizedName = "Secondary Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"SecondaryTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonYTouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonBTouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
// Trigger
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trigger",
|
||||
localizedName = "Trigger",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Trigger"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Trigger Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerPressed",
|
||||
localizedName = "Trigger Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Trigger Touch
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerTouched",
|
||||
localizedName = "Trigger Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumbstick Clicked
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickClicked",
|
||||
localizedName = "Thumbstick Clicked",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisClick"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickClick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumbstick Touched
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickTouched",
|
||||
localizedName = "Thumbstick Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumbrest Touched
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbrestTouched",
|
||||
localizedName = "Thumbrest Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"ThumbrestTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbrest,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Device Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Device Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Haptics
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "haptic",
|
||||
localizedName = "Haptic Output",
|
||||
type = ActionType.Vibrate,
|
||||
usages = new List<string>() { "Haptic" },
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = haptic,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Trigger Force
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerForce",
|
||||
localizedName = "Trigger Force",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerForce"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerForce,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Trigger Curl
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerCurl",
|
||||
localizedName = "Trigger Curl",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerCurl"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerCurl,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Trigger Slide
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerSlide",
|
||||
localizedName = "Trigger Slide",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerSlide"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerSlide,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Trigger Proximity
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerProximity",
|
||||
localizedName = "Trigger Proximity",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerProximity"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerProximity,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumb Proximity
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbProximity",
|
||||
localizedName = "Thumb Proximity",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"ThumbProximity"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbProximity,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b7365b139f7aec43b23d26b7a48b5a6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,982 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
#if USE_STICK_CONTROL_THUMBSTICKS
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.StickControl; // If replaced, make sure the control extends Vector2Control
|
||||
#else
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.Vector2Control;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of Meta Quest Pro controller interaction profiles in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Meta Quest Touch Pro Controller Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
|
||||
Company = "Unity",
|
||||
Desc = "Allows for mapping input to the Meta Quest Touch Pro Controller interaction profile.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/metaquesttouchprocontrollerprofile.html",
|
||||
OpenxrExtensionStrings = "XR_FB_touch_controller_pro",
|
||||
Version = "0.0.1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class MetaQuestTouchProControllerProfile : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.metaquestpro";
|
||||
|
||||
/// <summary>
|
||||
/// An Input System device based on the controller interaction profile Meta Touch Controller Pro.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Meta Quest Pro Touch Controller(OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class QuestProTouchController : XRControllerWithRumble
|
||||
{
|
||||
/// <summary>
|
||||
/// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control)/[StickControl](xref:UnityEngine.InputSystem.Controls.StickControl) that represents the <see cref="MetaQuestTouchProControllerProfile.thumbstick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary2DAxis", "Joystick" }, usage = "Primary2DAxis")]
|
||||
public ThumbstickControl thumbstick { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchProControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripAxis", "squeeze" }, usage = "Grip")]
|
||||
public AxisControl grip { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripButton", "squeezeClicked" }, usage = "GripButton")]
|
||||
public ButtonControl gripPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.system"/> <see cref="MetaQuestTouchProControllerProfile.menu"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary", "menuButton", "systemButton" }, usage = "MenuButton")]
|
||||
public ButtonControl menu { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.buttonA"/> <see cref="MetaQuestTouchProControllerProfile.buttonX"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "A", "X", "buttonA", "buttonX" }, usage = "PrimaryButton")]
|
||||
public ButtonControl primaryButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.buttonATouch"/> <see cref="MetaQuestTouchProControllerProfile.buttonYTouch"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "ATouched", "XTouched", "ATouch", "XTouch", "buttonATouched", "buttonXTouched" }, usage = "PrimaryTouch")]
|
||||
public ButtonControl primaryTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.buttonB"/> <see cref="MetaQuestTouchProControllerProfile.buttonY"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "B", "Y", "buttonB", "buttonY" }, usage = "SecondaryButton")]
|
||||
public ButtonControl secondaryButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.buttonBTouch"/> <see cref="MetaQuestTouchProControllerProfile.buttonYTouch"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "BTouched", "YTouched", "BTouch", "YTouch", "buttonBTouched", "buttonYTouched" }, usage = "SecondaryTouch")]
|
||||
public ButtonControl secondaryTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchProControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Trigger")]
|
||||
public AxisControl trigger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "indexButton", "indexTouched", "triggerbutton" }, usage = "TriggerButton")]
|
||||
public ButtonControl triggerPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.triggerTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "indexTouch", "indexNearTouched" }, usage = "TriggerTouch")]
|
||||
public ButtonControl triggerTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.thumbstickClick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "JoystickOrPadPressed", "thumbstickClick", "joystickClicked" }, usage = "Primary2DAxisClick")]
|
||||
public ButtonControl thumbstickClicked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.thumbstickTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "JoystickOrPadTouched", "thumbstickTouch", "joystickTouched" }, usage = "Primary2DAxisTouch")]
|
||||
public ButtonControl thumbstickTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.thumbrest"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "ThumbrestTouch")]
|
||||
public ButtonControl thumbrestTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="MetaQuestTouchProControllerProfile.grip"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="MetaQuestTouchProControllerProfile.aim"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 28, usage = "IsTracked")]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set to indicate what data is valid. This value is equivalent to mapping devicePose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 32, usage = "TrackingState")]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position. For the Oculus Touch device, this is both the grip and the pointer position. This value is equivalent to mapping devicePose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 36, noisy = true, alias = "gripPosition")]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation. For the Oculus Touch device, this is both the grip and the pointer rotation. This value is equivalent to mapping devicePose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 48, noisy = true, alias = "gripOrientation")]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for back compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 96)]
|
||||
public Vector3Control pointerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 108, alias = "pointerOrientation")]
|
||||
public QuaternionControl pointerRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="HapticControl"/> that represents the <see cref="MetaQuestTouchProControllerProfile.haptic"/> binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Haptic")]
|
||||
public HapticControl haptic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchProControllerProfile.thumbrestForce"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "ThumbrestForce")]
|
||||
public AxisControl thumbrestForce { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchProControllerProfile.stylusForce"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "StylusForce")]
|
||||
public AxisControl stylusForce { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchProControllerProfile.triggerCurl"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "TriggerCurl")]
|
||||
public AxisControl triggerCurl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MetaQuestTouchProControllerProfile.triggerSlide"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "TriggerSlide")]
|
||||
public AxisControl triggerSlide { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.triggerProximity"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "TriggerProximity")]
|
||||
public ButtonControl triggerProximity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MetaQuestTouchProControllerProfile.thumbProximity"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "ThumbProximity")]
|
||||
public ButtonControl thumbProximity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="HapticControl"/> that represents the <see cref="MetaQuestTouchProControllerProfile.hapticTrigger"/> binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "HapticTrigger")]
|
||||
public HapticControl hapticTrigger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="HapticControl"/> that represents the <see cref="MetaQuestTouchProControllerProfile.hapticThumb"/> binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "HapticThumb")]
|
||||
public HapticControl hapticThumb { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal call used to assign controls to the the correct element.
|
||||
/// </summary>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
|
||||
thumbstick = GetChildControl<StickControl>("thumbstick");
|
||||
trigger = GetChildControl<AxisControl>("trigger");
|
||||
triggerPressed = GetChildControl<ButtonControl>("triggerPressed");
|
||||
triggerTouched = GetChildControl<ButtonControl>("triggerTouched");
|
||||
grip = GetChildControl<AxisControl>("grip");
|
||||
gripPressed = GetChildControl<ButtonControl>("gripPressed");
|
||||
menu = GetChildControl<ButtonControl>("menu");
|
||||
primaryButton = GetChildControl<ButtonControl>("primaryButton");
|
||||
primaryTouched = GetChildControl<ButtonControl>("primaryTouched");
|
||||
secondaryButton = GetChildControl<ButtonControl>("secondaryButton");
|
||||
secondaryTouched = GetChildControl<ButtonControl>("secondaryTouched");
|
||||
thumbstickClicked = GetChildControl<ButtonControl>("thumbstickClicked");
|
||||
thumbstickTouched = GetChildControl<ButtonControl>("thumbstickTouched");
|
||||
thumbrestTouched = GetChildControl<ButtonControl>("thumbrestTouched");
|
||||
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
|
||||
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
|
||||
|
||||
haptic = GetChildControl<HapticControl>("haptic");
|
||||
thumbrestForce = GetChildControl<AxisControl>("thumbrestForce");
|
||||
stylusForce = GetChildControl<AxisControl>("stylusForce");
|
||||
triggerCurl = GetChildControl<AxisControl>("triggerCurl");
|
||||
triggerSlide = GetChildControl<AxisControl>("triggerSlide");
|
||||
triggerProximity = GetChildControl<ButtonControl>("triggerProximity");
|
||||
thumbProximity = GetChildControl<ButtonControl>("thumbProximity");
|
||||
hapticTrigger = GetChildControl<HapticControl>("hapticTrigger");
|
||||
hapticThumb = GetChildControl<HapticControl>("hapticThumb");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The interaction profile string used to reference Meta Quest Pro Touch Controller.
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/facebook/touch_controller_pro";
|
||||
|
||||
// Available Bindings
|
||||
// Left Hand Only
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/x/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonX = "/input/x/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/x/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonXTouch = "/input/x/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/y/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonY = "/input/y/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/y/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonYTouch = "/input/y/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/menu/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string menu = "/input/menu/click";
|
||||
|
||||
// Right Hand Only
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/a/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonA = "/input/a/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/a/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonATouch = "/input/a/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '..."/input/b/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonB = "/input/b/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/b/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonBTouch = "/input/b/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/system/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string system = "/input/system/click";
|
||||
|
||||
// Both Hands
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/squeeze/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string squeeze = "/input/squeeze/value";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trigger = "/input/trigger/value";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trigger/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerTouch = "/input/trigger/touch";
|
||||
/// <summary>
|
||||
/// Constant for a Vector2 interaction binding '...//input/thumbstick' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstick = "/input/thumbstick";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumbstick/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstickClick = "/input/thumbstick/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumbstick/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstickTouch = "/input/thumbstick/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumbrest/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbrest = "/input/thumbrest/touch";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string haptic = "/output/haptic";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/thumbrest/force' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbrestForce = "/input/thumbrest/force";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/stylus_fb/force' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string stylusForce = "/input/stylus_fb/force";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/curl_fb' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerCurl = "/input/trigger/curl_fb";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/slide_fb' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerSlide = "/input/trigger/slide_fb";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerProximity = "/input/trigger/proximity_fb";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbProximity = "/input/thumb_fb/proximity_fb";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string hapticTrigger = "/output/trigger_haptic_fb";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string hapticThumb = "/output/thumb_haptic_fb";
|
||||
|
||||
private const string kDeviceLocalizedName = "Meta Quest Pro Touch Controller OpenXR";
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override bool OnInstanceCreate(ulong instance)
|
||||
{
|
||||
// Requires the pro controller extension
|
||||
if (!OpenXRRuntime.IsExtensionEnabled("XR_FB_touch_controller_pro"))
|
||||
return false;
|
||||
|
||||
return base.OnInstanceCreate(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="QuestProTouchController"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(QuestProTouchController),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="QuestProTouchController"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(QuestProTouchController));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device for the Input System.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(QuestProTouchController);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "questprotouchcontroller",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "Oculus",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
// Joystick
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstick",
|
||||
localizedName = "Thumbstick",
|
||||
type = ActionType.Axis2D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxis"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Grip
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "grip",
|
||||
localizedName = "Grip",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Grip"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Grip Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "gripPressed",
|
||||
localizedName = "Grip Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GripButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Menu
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "menu",
|
||||
localizedName = "Menu",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"MenuButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = menu,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = system,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//A / X Press
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "primaryButton",
|
||||
localizedName = "Primary Button",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonX,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonA,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//A / X Touch
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "primaryTouched",
|
||||
localizedName = "Primary Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonXTouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonATouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//B / Y Press
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "secondaryButton",
|
||||
localizedName = "Secondary Button",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"SecondaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonY,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonB,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//B / Y Touch
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "secondaryTouched",
|
||||
localizedName = "Secondary Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"SecondaryTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonYTouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonBTouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
// Trigger
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trigger",
|
||||
localizedName = "Trigger",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Trigger"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Trigger Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerPressed",
|
||||
localizedName = "Trigger Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Trigger Touch
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerTouched",
|
||||
localizedName = "Trigger Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumbstick Clicked
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickClicked",
|
||||
localizedName = "Thumbstick Clicked",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisClick"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickClick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumbstick Touched
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickTouched",
|
||||
localizedName = "Thumbstick Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumbrest Touched
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbrestTouched",
|
||||
localizedName = "Thumbrest Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"ThumbrestTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbrest,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Device Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Device Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Haptics
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "haptic",
|
||||
localizedName = "Haptic Output",
|
||||
type = ActionType.Vibrate,
|
||||
usages = new List<string>() { "Haptic" },
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = haptic,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbrestForce",
|
||||
localizedName = "Thumbrest Force",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"ThumbrestForce"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbrestForce,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "stylusForce",
|
||||
localizedName = "Stylus Force",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"StylusForce"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = stylusForce,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerCurl",
|
||||
localizedName = "Trigger Curl",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerCurl"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerCurl,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerSlide",
|
||||
localizedName = "Trigger Slide",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerSlide"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerSlide,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Trigger Proximity
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerProximity",
|
||||
localizedName = "Trigger Proximity",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerProximity"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerProximity,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumb Proximity
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbProximity",
|
||||
localizedName = "Thumb Proximity",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"ThumbProximity"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbProximity,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Haptic Trigger
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "hapticTrigger",
|
||||
localizedName = "Haptic Trigger Output",
|
||||
type = ActionType.Vibrate,
|
||||
usages = new List<string>() { "HapticTrigger" },
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = hapticTrigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Haptic Thumb
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "hapticThumb",
|
||||
localizedName = "Haptic Thumb Output",
|
||||
type = ActionType.Vibrate,
|
||||
usages = new List<string>() { "HapticThumb" },
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = hapticThumb,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4b862ee14fb479fbfe5fffe655d3ed3
|
||||
timeCreated: 1667676951
|
||||
@@ -0,0 +1,353 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of Microsoft hand interaction profiles in OpenXR. It enables <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_MSFT_hand_interaction">XR_MSFT_hand_interaction</a> in the underyling runtime.
|
||||
/// This creates a new <see cref="InputDevice"/> with the <see cref="InputDeviceCharacteristics.HandTracking"/> characteristic.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Microsoft Hand Interaction Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android },
|
||||
Company = "Unity",
|
||||
Desc = "Allows for mapping input to the hand interaction profile. Will register the controller map for hand interaction if enabled.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/microsofthandinteraction.html",
|
||||
Version = "0.0.1",
|
||||
OpenxrExtensionStrings = extensionString,
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class MicrosoftHandInteraction : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.handtracking";
|
||||
|
||||
/// <summary>
|
||||
/// An Input System device based off the hand interaction profile in the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_MSFT_hand_interaction">Hand Interaction Extension</a>. Enabled through <see cref="MicrosoftHandInteraction"/>.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Hololens Hand (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class HoloLensHand : XRController
|
||||
{
|
||||
/// <summary>
|
||||
/// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MicrosoftHandInteraction.select"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "PrimaryAxis")]
|
||||
public AxisControl select { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MicrosoftHandInteraction.select"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary", "selectbutton" }, usages = new[] { "PrimaryButton" })]
|
||||
public ButtonControl selectPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MicrosoftHandInteraction.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(alias = "Secondary", usage = "Grip")]
|
||||
public AxisControl squeeze { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MicrosoftHandInteraction.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripButton", "squeezeClicked" }, usages = new[] { "GripButton" })]
|
||||
public ButtonControl squeezePressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the Microsoft Hand Interaction devicePose OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, alias = "device", usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the Microsoft Hand Interaction pointer OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 132)]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set to indicate what data is valid. This value is equivalent to mapping devicePose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 136)]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position, or grip position. This value is equivalent to mapping devicePose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 20, alias = "gripPosition")]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation, or grip orientation. This value is equivalent to mapping devicePose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 32, alias = "gripOrientation")]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 80)]
|
||||
public Vector3Control pointerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 92, alias = "pointerOrientation")]
|
||||
public QuaternionControl pointerRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal call used to assign controls to the the correct element.
|
||||
/// </summary>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
select = GetChildControl<AxisControl>("select");
|
||||
selectPressed = GetChildControl<ButtonControl>("selectPressed");
|
||||
squeeze = GetChildControl<AxisControl>("squeeze");
|
||||
squeezePressed = GetChildControl<ButtonControl>("squeezePressed");
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
|
||||
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The OpenXR Extension string. OpenXR uses this to check if this extension is available or enabled. See <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_MSFT_hand_interaction">hand interaction extension</a> documentation for more information on this OpenXR extension.</summary>
|
||||
public const string extensionString = "XR_MSFT_hand_interaction";
|
||||
|
||||
/// <summary>
|
||||
/// OpenXR string that represents the hand interaction profile.
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/microsoft/hand_interaction";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/select/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string select = "/input/select/value";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/menu/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string squeeze = "/input/squeeze/value";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
|
||||
private const string kDeviceLocalizedName = "HoloLens Hand OpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="HoloLensHand"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(HoloLensHand),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="HoloLensHand"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(HoloLensHand));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device for the Input System.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(HoloLensHand);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "microsofthandinteraction",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "Microsoft",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
// Select
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "select",
|
||||
localizedName = "Select",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryAxis"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = select,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Select Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "selectPressed",
|
||||
localizedName = "Select Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = select,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Squeeze
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "squeeze",
|
||||
localizedName = "Squeeze",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Grip"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Squeeze Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "squeezePressed",
|
||||
localizedName = "Squeeze Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GripButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Device Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Device Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f928d0d73a35f294fbe357ca17aa3547
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,560 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
#if USE_STICK_CONTROL_THUMBSTICKS
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.StickControl; // If replaced, make sure the control extends Vector2Control
|
||||
#else
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.Vector2Control;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of Microsoft Motion Controllers interaction profiles in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Microsoft Motion Controller Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA},
|
||||
Company = "Unity",
|
||||
Desc = "Allows for mapping input to the Microsoft Motion Controller interaction profile.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/microsoftmotioncontrollerprofile.html",
|
||||
OpenxrExtensionStrings = "",
|
||||
Version = "0.0.1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class MicrosoftMotionControllerProfile : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.microsoftmotioncontroller";
|
||||
|
||||
/// <summary>
|
||||
/// An Input System device based off the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#_microsoft_mixed_reality_motion_controller_profile">Microsoft Mixed Reality Motion Controller</a>.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Windows MR Controller (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class WMRSpatialController : XRControllerWithRumble
|
||||
{
|
||||
/// <summary>
|
||||
/// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control)/[StickControl](xref:UnityEngine.InputSystem.Controls.StickControl) that represents the <see cref="MicrosoftMotionControllerProfile.thumbstick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary2DAxis", "thumbstickaxes", "thumbstick" }, usage = "Primary2DAxis")]
|
||||
public ThumbstickControl joystick { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control)/[StickControl](xref:UnityEngine.InputSystem.Controls.StickControl) that represents the <see cref="MicrosoftMotionControllerProfile.trackpad"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Secondary2DAxis", "touchpadaxes", "trackpad" }, usage = "Secondary2DAxis")]
|
||||
public ThumbstickControl touchpad { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MicrosoftMotionControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripAxis", "squeeze" }, usage = "Grip")]
|
||||
public AxisControl grip { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MicrosoftMotionControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripButton", "squeezeClicked" }, usage = "GripButton")]
|
||||
public ButtonControl gripPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MicrosoftMotionControllerProfile.menu"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary", "menubutton" }, usage = "MenuButton")]
|
||||
public ButtonControl menu { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="MicrosoftMotionControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "triggeraxis" }, usage = "Trigger")]
|
||||
public AxisControl trigger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MicrosoftMotionControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(alias = "triggerbutton", usage = "TriggerButton")]
|
||||
public ButtonControl triggerPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MicrosoftMotionControllerProfile.thumbstickClick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "joystickClicked", "thumbstickpressed" }, usage = "Primary2DAxisClick")]
|
||||
public ButtonControl joystickClicked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MicrosoftMotionControllerProfile.trackpadClick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "joystickorpadpressed", "touchpadpressed", "trackpadClicked" }, usage = "Secondary2DAxisClick")]
|
||||
public ButtonControl touchpadClicked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="MicrosoftMotionControllerProfile.trackpadTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "joystickorpadtouched", "touchpadtouched", "trackpadTouched" }, usage = "Secondary2DAxisTouch")]
|
||||
public ButtonControl touchpadTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="MicrosoftMotionControllerProfile.grip"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="MicrosoftMotionControllerProfile.aim"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "aimPose" }, usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 32)]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set indicating what data is valid. This value is equivalent to mapping devicePose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 36)]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position, or grip position. This value is equivalent to mapping devicePose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 40, aliases = new[] { "gripPosition" })]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation, or grip orientation. This value is equivalent to mapping devicePose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 52, aliases = new[] { "gripOrientation" })]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for back compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 100)]
|
||||
public Vector3Control pointerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 112, aliases = new[] { "pointerOrientation" })]
|
||||
public QuaternionControl pointerRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="HapticControl"/> that represents the <see cref="MicrosoftMotionControllerProfile.haptic"/> binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Haptic")]
|
||||
public HapticControl haptic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal call used to assign controls to the the correct element.
|
||||
/// </summary>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
joystick = GetChildControl<StickControl>("joystick");
|
||||
trigger = GetChildControl<AxisControl>("trigger");
|
||||
touchpad = GetChildControl<StickControl>("touchpad");
|
||||
grip = GetChildControl<AxisControl>("grip");
|
||||
gripPressed = GetChildControl<ButtonControl>("gripPressed");
|
||||
menu = GetChildControl<ButtonControl>("menu");
|
||||
joystickClicked = GetChildControl<ButtonControl>("joystickClicked");
|
||||
triggerPressed = GetChildControl<ButtonControl>("triggerPressed");
|
||||
touchpadClicked = GetChildControl<ButtonControl>("touchpadClicked");
|
||||
touchpadTouched = GetChildControl<ButtonControl>("touchPadTouched");
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
|
||||
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
|
||||
|
||||
haptic = GetChildControl<HapticControl>("haptic");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The interaction profile string used to reference the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#_microsoft_mixed_reality_motion_controller_profile">Microsoft Mixed Reality Motion Controller</a>.
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/microsoft/motion_controller";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/menu/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string menu = "/input/menu/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/squeeze/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string squeeze = "/input/squeeze/click";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trigger = "/input/trigger/value";
|
||||
/// <summary>
|
||||
/// Constant for a Vector2 interaction binding '.../input/thumbstick' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstick = "/input/thumbstick";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumbstick/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstickClick = "/input/thumbstick/click";
|
||||
/// <summary>
|
||||
/// Constant for a Vector2 interaction binding '.../input/trackpad' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trackpad = "/input/trackpad";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trackpad/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trackpadClick = "/input/trackpad/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trackpad/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trackpadTouch = "/input/trackpad/touch";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string haptic = "/output/haptic";
|
||||
|
||||
private const string kDeviceLocalizedName = "Windows MR Controller OpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="WMRSpatialController"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(WMRSpatialController),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="WMRSpatialController"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(WMRSpatialController));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device for the Input System.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(WMRSpatialController);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "microsoftmotioncontroller",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "Microsoft",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
// Joystick
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "joystick",
|
||||
localizedName = "Joystick",
|
||||
type = ActionType.Axis2D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxis"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Touchpad
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "touchpad",
|
||||
localizedName = "Touchpad",
|
||||
type = ActionType.Axis2D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Secondary2DAxis"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpad,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Grip
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "grip",
|
||||
localizedName = "Grip",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Grip"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Grip Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "gripPressed",
|
||||
localizedName = "Grip Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GripButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Menu
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "menu",
|
||||
localizedName = "Menu",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"MenuButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = menu,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Trigger
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trigger",
|
||||
localizedName = "Trigger",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Trigger"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Trigger Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerPressed",
|
||||
localizedName = "Trigger Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Joystick Clicked
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "joystickClicked",
|
||||
localizedName = "JoystickClicked",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisClick"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickClick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Touchpad Clicked
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "touchpadClicked",
|
||||
localizedName = "Touchpad Clicked",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Secondary2DAxisClick"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadClick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Touchpad Touched
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "touchpadTouched",
|
||||
localizedName = "Touchpad Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Secondary2DAxisTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Device Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Device Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Haptics
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "haptic",
|
||||
localizedName = "Haptic Output",
|
||||
type = ActionType.Vibrate,
|
||||
usages = new List<string>() { "Haptic" },
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = haptic,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 761fdd4502cb7a84e9ec7a2b24f33f37
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,741 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
#if USE_STICK_CONTROL_THUMBSTICKS
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.StickControl; // If replaced, make sure the control extends Vector2Control
|
||||
#else
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.Vector2Control;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of Oculus TouchControllers interaction profiles in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Oculus Touch Controller Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
|
||||
Company = "Unity",
|
||||
Desc = "Allows for mapping input to the Oculus Touch Controller interaction profile.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/oculustouchcontrollerprofile.html",
|
||||
OpenxrExtensionStrings = "",
|
||||
Version = "0.0.1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class OculusTouchControllerProfile : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.oculustouch";
|
||||
|
||||
/// <summary>
|
||||
/// An Input System device based on the hand interaction profile in the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#_oculus_touch_controller_profile">Oculus Touch Controller</a>.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Oculus Touch Controller (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class OculusTouchController : XRControllerWithRumble
|
||||
{
|
||||
/// <summary>
|
||||
/// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control)/[StickControl](xref:UnityEngine.InputSystem.Controls.StickControl) that represents the <see cref="OculusTouchControllerProfile.thumbstick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary2DAxis", "Joystick" }, usage = "Primary2DAxis")]
|
||||
public ThumbstickControl thumbstick { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="OculusTouchControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripAxis", "squeeze" }, usage = "Grip")]
|
||||
public AxisControl grip { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripButton", "squeezeClicked" }, usage = "GripButton")]
|
||||
public ButtonControl gripPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.system"/> <see cref="OculusTouchControllerProfile.menu"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "Primary", "menuButton", "systemButton" }, usage = "MenuButton")]
|
||||
public ButtonControl menu { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.buttonA"/> <see cref="OculusTouchControllerProfile.buttonX"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "A", "X", "buttonA", "buttonX" }, usage = "PrimaryButton")]
|
||||
public ButtonControl primaryButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.buttonATouch"/> <see cref="OculusTouchControllerProfile.buttonYTouch"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "ATouched", "XTouched", "ATouch", "XTouch", "buttonATouched", "buttonXTouched" }, usage = "PrimaryTouch")]
|
||||
public ButtonControl primaryTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.buttonB"/> <see cref="OculusTouchControllerProfile.buttonY"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "B", "Y", "buttonB", "buttonY" }, usage = "SecondaryButton")]
|
||||
public ButtonControl secondaryButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.buttonBTouch"/> <see cref="OculusTouchControllerProfile.buttonYTouch"/> OpenXR bindings, depending on handedness.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "BTouched", "YTouched", "BTouch", "YTouch", "buttonBTouched", "buttonYTouched" }, usage = "SecondaryTouch")]
|
||||
public ButtonControl secondaryTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="OculusTouchControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Trigger")]
|
||||
public AxisControl trigger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "indexButton", "indexTouched", "triggerbutton" }, usage = "TriggerButton")]
|
||||
public ButtonControl triggerPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.triggerTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "indexTouch", "indexNearTouched" }, usage = "TriggerTouch")]
|
||||
public ButtonControl triggerTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.thumbstickClick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "JoystickOrPadPressed", "thumbstickClick", "joystickClicked" }, usage = "Primary2DAxisClick")]
|
||||
public ButtonControl thumbstickClicked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.thumbstickTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "JoystickOrPadTouched", "thumbstickTouch", "joystickTouched" }, usage = "Primary2DAxisTouch")]
|
||||
public ButtonControl thumbstickTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="OculusTouchControllerProfile.thumbrest"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "ThumbrestTouch")]
|
||||
public ButtonControl thumbrestTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="OculusTouchControllerProfile.grip"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="OculusTouchControllerProfile.aim"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 28, usage = "IsTracked")]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set to indicate what data is valid. This value is equivalent to mapping devicePose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 32, usage = "TrackingState")]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position. For the Oculus Touch device, this is both the grip and the pointer position. This value is equivalent to mapping devicePose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 36, noisy = true, alias = "gripPosition")]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation. For the Oculus Touch device, this is both the grip and the pointer rotation. This value is equivalent to mapping devicePose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 48, noisy = true, alias = "gripOrientation")]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for back compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 96)]
|
||||
public Vector3Control pointerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 108, alias = "pointerOrientation")]
|
||||
public QuaternionControl pointerRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="HapticControl"/> that represents the <see cref="OculusTouchControllerProfile.haptic"/> binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Haptic")]
|
||||
public HapticControl haptic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal call used to assign controls to the the correct element.
|
||||
/// </summary>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
thumbstick = GetChildControl<StickControl>("thumbstick");
|
||||
trigger = GetChildControl<AxisControl>("trigger");
|
||||
triggerPressed = GetChildControl<ButtonControl>("triggerPressed");
|
||||
triggerTouched = GetChildControl<ButtonControl>("triggerTouched");
|
||||
grip = GetChildControl<AxisControl>("grip");
|
||||
gripPressed = GetChildControl<ButtonControl>("gripPressed");
|
||||
menu = GetChildControl<ButtonControl>("menu");
|
||||
primaryButton = GetChildControl<ButtonControl>("primaryButton");
|
||||
primaryTouched = GetChildControl<ButtonControl>("primaryTouched");
|
||||
secondaryButton = GetChildControl<ButtonControl>("secondaryButton");
|
||||
secondaryTouched = GetChildControl<ButtonControl>("secondaryTouched");
|
||||
thumbstickClicked = GetChildControl<ButtonControl>("thumbstickClicked");
|
||||
thumbstickTouched = GetChildControl<ButtonControl>("thumbstickTouched");
|
||||
thumbrestTouched = GetChildControl<ButtonControl>("thumbrestTouched");
|
||||
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
|
||||
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
|
||||
|
||||
haptic = GetChildControl<HapticControl>("haptic");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The interaction profile string used to reference the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#_oculus_touch_controller_profile">Oculus Touch Controller</a>.
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/oculus/touch_controller";
|
||||
|
||||
// Available Bindings
|
||||
// Left Hand Only
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/x/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonX = "/input/x/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/x/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonXTouch = "/input/x/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/y/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonY = "/input/y/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/y/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonYTouch = "/input/y/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/menu/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.leftHand"/> user path.
|
||||
/// </summary>
|
||||
public const string menu = "/input/menu/click";
|
||||
|
||||
// Right Hand Only
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/a/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonA = "/input/a/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/a/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonATouch = "/input/a/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '..."/input/b/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonB = "/input/b/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/b/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string buttonBTouch = "/input/b/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/system/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the <see cref="OpenXRInteractionFeature.UserPaths.rightHand"/> user path.
|
||||
/// </summary>
|
||||
public const string system = "/input/system/click";
|
||||
|
||||
// Both Hands
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string squeeze = "/input/squeeze/value";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string trigger = "/input/trigger/value";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string triggerTouch = "/input/trigger/touch";
|
||||
/// <summary>
|
||||
/// Constant for a Vector2 interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstick = "/input/thumbstick";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstickClick = "/input/thumbstick/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbstickTouch = "/input/thumbstick/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string thumbrest = "/input/thumbrest/touch";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
|
||||
/// </summary>
|
||||
public const string haptic = "/output/haptic";
|
||||
|
||||
private const string kDeviceLocalizedName = "Oculus Touch Controller OpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="OculusTouchController"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(OculusTouchController),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="OculusTouchController"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(OculusTouchController));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device for the Input System.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(OculusTouchController);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "oculustouchcontroller",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "Oculus",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
// Joystick
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstick",
|
||||
localizedName = "Thumbstick",
|
||||
type = ActionType.Axis2D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxis"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Grip
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "grip",
|
||||
localizedName = "Grip",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Grip"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Grip Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "gripPressed",
|
||||
localizedName = "Grip Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GripButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Menu
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "menu",
|
||||
localizedName = "Menu",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"MenuButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = menu,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = system,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//A / X Press
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "primaryButton",
|
||||
localizedName = "Primary Button",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonX,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonA,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//A / X Touch
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "primaryTouched",
|
||||
localizedName = "Primary Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonXTouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonATouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//B / Y Press
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "secondaryButton",
|
||||
localizedName = "Secondary Button",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"SecondaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonY,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonB,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
//B / Y Touch
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "secondaryTouched",
|
||||
localizedName = "Secondary Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"SecondaryTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonYTouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.leftHand }
|
||||
},
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonBTouch,
|
||||
interactionProfileName = profile,
|
||||
userPaths = new List<string>() { UserPaths.rightHand }
|
||||
},
|
||||
}
|
||||
},
|
||||
// Trigger
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trigger",
|
||||
localizedName = "Trigger",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Trigger"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Trigger Pressed
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerPressed",
|
||||
localizedName = "Trigger Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Trigger Touch
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerTouched",
|
||||
localizedName = "Trigger Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumbstick Clicked
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickClicked",
|
||||
localizedName = "Thumbstick Clicked",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisClick"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickClick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumbstick Touched
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickTouched",
|
||||
localizedName = "Thumbstick Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
//Thumbrest Touched
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbrestTouched",
|
||||
localizedName = "Thumbrest Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"ThumbrestTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbrest,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Device Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Device Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Haptics
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "haptic",
|
||||
localizedName = "Haptic Output",
|
||||
type = ActionType.Vibrate,
|
||||
usages = new List<string>() { "Haptic" },
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = haptic,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: feeef8d85de8db242bdda70cc7ff5acd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,296 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of Palm Pose feature in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Palm Pose",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
|
||||
Company = "Unity",
|
||||
Desc = "Add Palm pose feature and if enabled, extra palm pose path /input/palm_ext/pose will be added to regular interaction profile.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/palmposeinteraction.html",
|
||||
OpenxrExtensionStrings = extensionString,
|
||||
Version = "0.0.1",
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class PalmPoseInteraction : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.palmpose";
|
||||
|
||||
/// <summary>
|
||||
/// A flag to mark this Palm Pose feature is additive.
|
||||
/// </summary>
|
||||
internal override bool IsAdditive => true;
|
||||
|
||||
/// <summary>
|
||||
/// Palm Pose interaction feature supports an input patch for the palm pose.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Palm Pose (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class PalmPose : XRController
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="PalmPoseInteraction.palmPose"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0)]
|
||||
public PoseControl palmPose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping palmPose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0)]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set to indicate what data is valid. This value is equivalent to mapping palmPose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 4)]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position. This value is equivalent to mapping palmPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 8, noisy = true, alias = "palmPosition")]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation. This value is equivalent to mapping palmPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 20, noisy = true, alias = "palmRotation")]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the palm pose position. This value is equivalent to mapping palmPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 8, noisy = true)]
|
||||
public Vector3Control palmPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the palm pose orientation. This value is equivalent to mapping palmPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 20, noisy = true)]
|
||||
public QuaternionControl palmRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal call used to assign controls to the the correct element.
|
||||
/// </summary>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
palmPose = GetChildControl<PoseControl>("palmPose");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../palm_ext/pose' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string palmPose = "/input/palm_ext/pose";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../grip_surface/pose' OpenXR Input Binding, which is supported in OpenXR 1.1 specification
|
||||
/// </summary>
|
||||
public const string gripSurfacePose = "/input/grip_surface/pose";
|
||||
|
||||
/// <summary>
|
||||
/// A unique string for palm pose feature
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/ext/palmpose";
|
||||
|
||||
private const string kDeviceLocalizedName = "Palm Pose Interaction OpenXR";
|
||||
/// <summary>
|
||||
/// The OpenXR Extension string. This is used by OpenXR to check if this extension is available or enabled.
|
||||
/// </summary>
|
||||
public const string extensionString = "XR_EXT_palm_pose";
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected internal override void GetValidationChecks(List<OpenXRFeature.ValidationRule> results, BuildTargetGroup target)
|
||||
{
|
||||
results.Add( new ValidationRule(this){
|
||||
message = "Additive Interaction feature requires a valid controller or hand interaction profile selected within Interaction Profiles.",
|
||||
error = true,
|
||||
errorEnteringPlaymode = true,
|
||||
checkPredicate = () =>
|
||||
{
|
||||
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(target);
|
||||
if (null == settings)
|
||||
return false;
|
||||
|
||||
bool palmPoseFeatureEnabled = false;
|
||||
bool otherNonAdditiveInteractionFeatureEnabled = false;
|
||||
foreach (var feature in settings.GetFeatures<OpenXRInteractionFeature>())
|
||||
{
|
||||
if (feature.enabled)
|
||||
{
|
||||
if (feature is PalmPoseInteraction)
|
||||
palmPoseFeatureEnabled = true;
|
||||
else if (!(feature as OpenXRInteractionFeature).IsAdditive && !(feature is EyeGazeInteraction))
|
||||
otherNonAdditiveInteractionFeatureEnabled = true;
|
||||
}
|
||||
}
|
||||
return palmPoseFeatureEnabled && otherNonAdditiveInteractionFeatureEnabled;
|
||||
},
|
||||
fixIt = () => SettingsService.OpenProjectSettings("Project/XR Plug-in Management/OpenXR"),
|
||||
fixItAutomatic = false,
|
||||
fixItMessage = "Open Project Settings to select one or more non Additive interaction profiles."
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override bool OnInstanceCreate(ulong instance)
|
||||
{
|
||||
// Requires the palmPose extension
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(extensionString))
|
||||
return false;
|
||||
|
||||
return base.OnInstanceCreate(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="PalmPose"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(PalmPose),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="PalmPose"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(PalmPose));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device for the Input System.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(PalmPose);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "palmposeinteraction",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "palmpose",
|
||||
localizedName = "Palm Pose",
|
||||
type = ActionType.Pose,
|
||||
bindings = AddBindingBasedOnRuntimeAPIVersion(),
|
||||
isAdditive = true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine path binding based on current runtime API version.
|
||||
/// </summary>
|
||||
internal List<ActionBinding> AddBindingBasedOnRuntimeAPIVersion()
|
||||
{
|
||||
List<ActionBinding> pairingActionBinding;
|
||||
if (OpenXRRuntime.isRuntimeAPIVersionGreaterThan1_1())
|
||||
{
|
||||
pairingActionBinding = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = gripSurfacePose,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
pairingActionBinding = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = palmPose,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return pairingActionBinding;
|
||||
}
|
||||
/// <summary>
|
||||
/// Process additive actions: add additional supported additive actions to existing controller profiles
|
||||
/// </summary>
|
||||
internal override void AddAdditiveActions(List<OpenXRInteractionFeature.ActionMapConfig> actionMaps, ActionMapConfig additiveMap)
|
||||
{
|
||||
foreach (var actionMap in actionMaps)
|
||||
{
|
||||
//valid userPath is user/hand/left or user/hand/right
|
||||
var validUserPath = actionMap.deviceInfos.Where(d => d.userPath != null && ((String.CompareOrdinal(d.userPath, OpenXRInteractionFeature.UserPaths.leftHand) == 0) ||
|
||||
(String.CompareOrdinal(d.userPath, OpenXRInteractionFeature.UserPaths.rightHand) == 0)));
|
||||
if (!validUserPath.Any())
|
||||
continue;
|
||||
|
||||
foreach (var additiveAction in additiveMap.actions.Where(a => a.isAdditive))
|
||||
{
|
||||
actionMap.actions.Add(additiveAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f028123e2efe1d443875bc7609b4a98b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,785 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
|
||||
#if USE_STICK_CONTROL_THUMBSTICKS
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.StickControl; // If replaced, make sure the control extends Vector2Control
|
||||
#else
|
||||
using ThumbstickControl = UnityEngine.InputSystem.Controls.Vector2Control;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of Valve Index Controllers interaction profiles in OpenXR.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Valve Index Controller Profile",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA },
|
||||
Company = "Unity",
|
||||
Desc = "Allows for mapping input to the Valve Index Controller interaction profile.",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/valveindexcontrollerprofile.html",
|
||||
OpenxrExtensionStrings = "",
|
||||
Version = "0.0.1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class ValveIndexControllerProfile : OpenXRInteractionFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.unity.openxr.feature.input.valveindex";
|
||||
|
||||
/// <summary>
|
||||
/// An Input System device based on the hand interaction profile in the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#_valve_index_controller_profile">Valve Index Controller</a>.
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = "Index Controller (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class ValveIndexController : XRControllerWithRumble
|
||||
{
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.system"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(alias = "systemButton", usage = "MenuButton")]
|
||||
public ButtonControl system { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.systemTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "MenuTouch")]
|
||||
public ButtonControl systemTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.buttonA"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "PrimaryButton")]
|
||||
public ButtonControl primaryButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.buttonATouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "PrimaryTouch")]
|
||||
public ButtonControl primaryTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.buttonB"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "SecondaryButton")]
|
||||
public ButtonControl secondaryButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.buttonBTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "SecondaryTouch")]
|
||||
public ButtonControl secondaryTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="ValveIndexControllerProfile.squeeze"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripAxis", "squeeze" }, usage = "Grip")]
|
||||
public AxisControl grip { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the Valve Index Controller Profile gripPressed OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "GripButton", "squeezeClicked" }, usage = "GripButton")]
|
||||
public ButtonControl gripPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="ValveIndexControllerProfile.squeezeForce"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(alias = "squeezeForce", usage = "GripForce")]
|
||||
public AxisControl gripForce { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="ValveIndexControllerProfile.trigger"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Trigger")]
|
||||
public AxisControl trigger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.triggerClick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "TriggerButton")]
|
||||
public ButtonControl triggerPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.triggerTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "TriggerTouch")]
|
||||
public ButtonControl triggerTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control)/[StickControl](xref:UnityEngine.InputSystem.Controls.StickControl) that represents the <see cref="ValveIndexControllerProfile.thumbstick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "joystick", "Primary2DAxis" }, usage = "Primary2DAxis")]
|
||||
public ThumbstickControl thumbstick { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.thumbstickClick"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(alias = "joystickClicked", usage = "Primary2DAxisClick")]
|
||||
public ButtonControl thumbstickClicked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.thumbstickTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(alias = "joystickTouched", usage = "Primary2DAxisTouch")]
|
||||
public ButtonControl thumbstickTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control)/[StickControl](xref:UnityEngine.InputSystem.Controls.StickControl) that represents the <see cref="ValveIndexControllerProfile.trackpad"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(aliases = new[] { "touchpad", "Secondary2DAxis" }, usage = "Secondary2DAxis")]
|
||||
public ThumbstickControl trackpad { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the <see cref="ValveIndexControllerProfile.trackpadTouch"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(alias = "touchpadTouched", usage = "Secondary2DAxisTouch")]
|
||||
public ButtonControl trackpadTouched { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the <see cref="ValveIndexControllerProfile.trackpadForce"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(alias = "touchpadForce", usage = "Secondary2DAxisForce")]
|
||||
public AxisControl trackpadForce { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="ValveIndexControllerProfile.grip"/> OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the Valve Index Controller Profile pointer OpenXR binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")]
|
||||
public PoseControl pointer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 53)]
|
||||
new public ButtonControl isTracked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set indicating what data is valid. This value is equivalent to mapping devicePose/trackingState.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 56)]
|
||||
new public IntegerControl trackingState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position, or grip position. This value is equivalent to mapping devicePose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 60, alias = "gripPosition")]
|
||||
new public Vector3Control devicePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation, or grip orientation. This value is equivalent to mapping devicePose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 72, alias = "gripOrientation")]
|
||||
new public QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 120)]
|
||||
public Vector3Control pointerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 132, alias = "pointerOrientation")]
|
||||
public QuaternionControl pointerRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="HapticControl"/> that represents the <see cref="ValveIndexControllerProfile.haptic"/> binding.
|
||||
/// </summary>
|
||||
[Preserve, InputControl(usage = "Haptic")]
|
||||
public HapticControl haptic { get; private set; }
|
||||
|
||||
/// <inheritdoc cref="OpenXRDevice"/>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
system = GetChildControl<ButtonControl>("system");
|
||||
systemTouched = GetChildControl<ButtonControl>("systemTouched");
|
||||
primaryButton = GetChildControl<ButtonControl>("primaryButton");
|
||||
primaryTouched = GetChildControl<ButtonControl>("primaryTouched");
|
||||
secondaryButton = GetChildControl<ButtonControl>("secondaryButton");
|
||||
secondaryTouched = GetChildControl<ButtonControl>("secondaryTouched");
|
||||
grip = GetChildControl<AxisControl>("grip");
|
||||
gripPressed = GetChildControl<ButtonControl>("gripPressed");
|
||||
gripForce = GetChildControl<AxisControl>("gripForce");
|
||||
trigger = GetChildControl<AxisControl>("trigger");
|
||||
triggerPressed = GetChildControl<ButtonControl>("triggerPressed");
|
||||
triggerTouched = GetChildControl<ButtonControl>("triggerTouched");
|
||||
thumbstick = GetChildControl<StickControl>("thumbstick");
|
||||
thumbstickClicked = GetChildControl<ButtonControl>("thumbstickClicked");
|
||||
thumbstickTouched = GetChildControl<ButtonControl>("thumbstickTouched");
|
||||
trackpad = GetChildControl<StickControl>("trackpad");
|
||||
trackpadTouched = GetChildControl<ButtonControl>("trackpadTouched");
|
||||
trackpadForce = GetChildControl<AxisControl>("trackpadForce");
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
pointer = GetChildControl<PoseControl>("pointer");
|
||||
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
|
||||
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
|
||||
|
||||
haptic = GetChildControl<HapticControl>("haptic");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The interaction profile string used to reference the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#_valve_index_controller_profile">Valve Index Controller</a>.
|
||||
/// </summary>
|
||||
public const string profile = "/interaction_profiles/valve/index_controller";
|
||||
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/system/click' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string system = "/input/system/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/system/touch' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string systemTouch = "/input/system/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/a/click' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string buttonA = "/input/a/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/a/touch' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string buttonATouch = "/input/a/touch";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/b/click' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string buttonB = "/input/b/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/b/touch' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string buttonBTouch = "/input/b/touch";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/squeeze/value' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string squeeze = "/input/squeeze/value";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/squeeze/force' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string squeezeForce = "/input/squeeze/force";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trigger/click' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string triggerClick = "/input/trigger/click";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trigger/value' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string trigger = "/input/trigger/value";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trigger/touch' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string triggerTouch = "/input/trigger/touch";
|
||||
/// <summary>
|
||||
/// Constant for a Vector2 interaction binding '.../input/thumbstick' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string thumbstick = "/input/thumbstick";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumbstick/click' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string thumbstickClick = "/input/thumbstick/click";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/thumbstick/touch' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string thumbstickTouch = "/input/thumbstick/touch";
|
||||
/// <summary>
|
||||
/// Constant for a Vector2 interaction binding '.../input/trackpad' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string trackpad = "/input/trackpad";
|
||||
/// <summary>
|
||||
/// Constant for a float interaction binding '.../input/trackpad/force' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string trackpadForce = "/input/trackpad/force";
|
||||
/// <summary>
|
||||
/// Constant for a boolean interaction binding '.../input/trackpad/touch' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string trackpadTouch = "/input/trackpad/touch";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string grip = "/input/grip/pose";
|
||||
/// <summary>
|
||||
/// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string aim = "/input/aim/pose";
|
||||
/// <summary>
|
||||
/// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding.
|
||||
/// </summary>
|
||||
public const string haptic = "/output/haptic";
|
||||
|
||||
private const string kDeviceLocalizedName = "Index Controller OpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="ValveIndexController"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(ValveIndexController),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="ValveIndexController"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return;
|
||||
#endif
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(ValveIndexController));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout string that used for registering device for the Input System.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(ValveIndexController);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = "valveindexcontroller",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "Valve",
|
||||
serialNumber = "",
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left),
|
||||
userPath = UserPaths.leftHand
|
||||
},
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right),
|
||||
userPath = UserPaths.rightHand
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
{
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "system",
|
||||
localizedName = "System",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"MenuButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = system,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "systemTouched",
|
||||
localizedName = "System Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"MenuTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = systemTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "primaryButton",
|
||||
localizedName = "Primary Button",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonA,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "primaryTouched",
|
||||
localizedName = "Primary Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"PrimaryTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonATouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "secondaryButton",
|
||||
localizedName = "Secondary Button",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"SecondaryButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonB,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "secondaryTouched",
|
||||
localizedName = "Secondary Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"SecondaryTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = buttonBTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "grip",
|
||||
localizedName = "Grip",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Grip"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "gripPressed",
|
||||
localizedName = "Grip Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GripButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeeze,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "gripForce",
|
||||
localizedName = "Grip Force",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"GripForce"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = squeezeForce,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trigger",
|
||||
localizedName = "Trigger",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Trigger"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trigger,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerPressed",
|
||||
localizedName = "Triggger Pressed",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerButton"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerClick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "triggerTouched",
|
||||
localizedName = "Trigger Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"TriggerTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = triggerTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstick",
|
||||
localizedName = "Thumbstick",
|
||||
type = ActionType.Axis2D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxis"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickClicked",
|
||||
localizedName = "Thumbstick Clicked",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisClick"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickClick,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "thumbstickTouched",
|
||||
localizedName = "Thumbstick Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Primary2DAxisTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = thumbstickTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpad",
|
||||
localizedName = "Trackpad",
|
||||
type = ActionType.Axis2D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Secondary2DAxis"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpad,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpadForce",
|
||||
localizedName = "Trackpad Force",
|
||||
type = ActionType.Axis1D,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Secondary2DAxisForce"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadForce,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "trackpadTouched",
|
||||
localizedName = "Trackpad Touched",
|
||||
type = ActionType.Binary,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Secondary2DAxisTouch"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = trackpadTouch,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Device Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "devicePose",
|
||||
localizedName = "Device Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Device"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = grip,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Pointer Pose
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "pointer",
|
||||
localizedName = "Pointer Pose",
|
||||
type = ActionType.Pose,
|
||||
usages = new List<string>()
|
||||
{
|
||||
"Pointer"
|
||||
},
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = aim,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Haptics
|
||||
new ActionConfig()
|
||||
{
|
||||
name = "haptic",
|
||||
localizedName = "Haptic Output",
|
||||
type = ActionType.Vibrate,
|
||||
usages = new List<string>() { "Haptic" },
|
||||
bindings = new List<ActionBinding>()
|
||||
{
|
||||
new ActionBinding()
|
||||
{
|
||||
interactionPath = haptic,
|
||||
interactionProfileName = profile,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddActionMap(actionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d6ccd3d0ef0f1d458e69421dccbdae1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
823
Packages/com.unity.xr.openxr/Runtime/Features/OpenXRFeature.cs
Normal file
823
Packages/com.unity.xr.openxr/Runtime/Features/OpenXRFeature.cs
Normal file
@@ -0,0 +1,823 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.OpenXR;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.XR.OpenXR.Editor")]
|
||||
[assembly: InternalsVisibleTo("UnityEditor.XR.OpenXR.Tests")]
|
||||
namespace UnityEngine.XR.OpenXR.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// A Unity OpenXR Feature.
|
||||
/// This class can be inherited from to add feature specific data and logic.
|
||||
/// Feature-specific settings are serialized for access at runtime.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract partial class OpenXRFeature : ScriptableObject
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
internal static Func<string, bool> canSetFeatureDisabled;
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Feature will be enabled when OpenXR is initialized.
|
||||
/// </summary>
|
||||
[FormerlySerializedAs("enabled")] [HideInInspector] [SerializeField] private bool m_enabled = false;
|
||||
|
||||
internal bool failedInitialization { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// True if a required feature failed initialization, false if all features initialized successfully.
|
||||
/// </summary>
|
||||
internal static bool requiredFeatureFailed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Feature is enabled and will be started when the OpenXR loader is initialized.
|
||||
///
|
||||
/// Note that the enabled state of a feature cannot be modified once OpenXR is initialized and
|
||||
/// can be used at runtime to determine if a feature successfully initialized.
|
||||
/// </summary>
|
||||
public bool enabled
|
||||
{
|
||||
get => m_enabled && (OpenXRLoaderBase.Instance == null || !failedInitialization);
|
||||
set
|
||||
{
|
||||
if (enabled == value)
|
||||
return;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (canSetFeatureDisabled != null && !value && !canSetFeatureDisabled.Invoke(featureIdInternal))
|
||||
return;
|
||||
#endif //UNITY_EDITOR
|
||||
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
{
|
||||
Debug.LogError("OpenXRFeature.enabled cannot be changed while OpenXR is running");
|
||||
return;
|
||||
}
|
||||
|
||||
m_enabled = value;
|
||||
|
||||
OnEnabledChange();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Automatically filled out by the build process from OpenXRFeatureAttribute.
|
||||
/// Name of the feature.
|
||||
/// </summary>
|
||||
[HideInInspector] [SerializeField] internal string nameUi = null;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically filled out by the build process from OpenXRFeatureAttribute.
|
||||
/// Version of the feature.
|
||||
/// </summary>
|
||||
[HideInInspector] [SerializeField] internal string version = null;
|
||||
|
||||
/// <summary>
|
||||
/// Feature id.
|
||||
/// </summary>
|
||||
[HideInInspector] [SerializeField] internal string featureIdInternal = null;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically filled out by the build process from OpenXRFeatureAttribute.
|
||||
/// OpenXR runtime extension strings that need to be enabled to use this extension.
|
||||
/// May contain multiple extensions separated by spaces.
|
||||
/// </summary>
|
||||
[HideInInspector] [SerializeField] internal string openxrExtensionStrings = null;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically filled out by the build process from OpenXRFeatureAttribute.
|
||||
/// Company name of the author of the feature.
|
||||
/// </summary>
|
||||
[HideInInspector] [SerializeField] internal string company = null;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically filled out by the build process from OpenXRFeatureAttribute.
|
||||
/// Priority of the feature.
|
||||
/// </summary>
|
||||
[HideInInspector] [SerializeField] internal int priority = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically filled out by the build process from OpenXRFeatureAttribute.
|
||||
/// True if the feature is required, false otherwise.
|
||||
/// </summary>
|
||||
[HideInInspector] [SerializeField] internal bool required = false;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if the internal fields have been updated in the current domain
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
internal bool internalFieldsUpdated = false;
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for xrGetInstanceProcAddr function pointer.
|
||||
/// </summary>
|
||||
protected static IntPtr xrGetInstanceProcAddr => Internal_GetProcAddressPtr(false);
|
||||
|
||||
/// <summary>
|
||||
/// Called to hook xrGetInstanceProcAddr.
|
||||
/// Returning a different function pointer allows intercepting any OpenXR method.
|
||||
/// </summary>
|
||||
/// <param name="func">xrGetInstanceProcAddr native function pointer</param>
|
||||
/// <returns>Function pointer that Unity will use to look up OpenXR native functions.</returns>
|
||||
protected internal virtual IntPtr HookGetInstanceProcAddr(IntPtr func) => func;
|
||||
|
||||
/// <summary>
|
||||
/// Called after the OpenXR Loader is initialized and has created its subsystems.
|
||||
/// </summary>
|
||||
protected internal virtual void OnSubsystemCreate() { }
|
||||
|
||||
/// <summary>
|
||||
/// Called after the OpenXR loader has started its subsystems.
|
||||
/// </summary>
|
||||
protected internal virtual void OnSubsystemStart() { }
|
||||
|
||||
/// <summary>
|
||||
/// Called before the OpenXR loader stops its subsystems.
|
||||
/// </summary>
|
||||
protected internal virtual void OnSubsystemStop() { }
|
||||
|
||||
/// <summary>
|
||||
/// Called before the OpenXR loader destroys its subsystems.
|
||||
/// </summary>
|
||||
protected internal virtual void OnSubsystemDestroy() { }
|
||||
|
||||
/// <summary>
|
||||
/// Called after `xrCreateInstance`. Override this method to validate that any necessary OpenXR extensions were
|
||||
/// successfully enabled (<see cref="OpenXRRuntime.IsExtensionEnabled">OpenXRRuntime.IsExtensionEnabled</see>)
|
||||
/// and that any required system properties are supported. If this method returns <see langword="false"/>,
|
||||
/// the feature's <see cref="OpenXRFeature.enabled"/> property is set to <see langword="false"/>.
|
||||
/// </summary>
|
||||
/// <param name="xrInstance">Handle of the native `xrInstance`.</param>
|
||||
/// <returns><see langword="true"/> if this feature successfully initialized. Otherwise, <see langword="false"/>.</returns>
|
||||
/// <remarks>
|
||||
/// If this feature is a required feature of an enabled feature set, returning <see langword="false"/> here
|
||||
/// causes the `OpenXRLoader` to fail, and XR Plug-in Management will fall back to another loader if enabled.
|
||||
/// </remarks>
|
||||
/// <seealso href="xref:openxr-features#enabling-openxr-spec-extension-strings">Enabling OpenXR spec extension strings</seealso>
|
||||
protected internal virtual bool OnInstanceCreate(ulong xrInstance) => true;
|
||||
|
||||
/// <summary>
|
||||
/// Called after xrGetSystem.
|
||||
/// </summary>
|
||||
/// <param name="xrSystem">Handle of the xrSystemId</param>
|
||||
protected internal virtual void OnSystemChange(ulong xrSystem) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called after xrCreateSession.
|
||||
/// </summary>
|
||||
/// <param name="xrSession">Handle of the xrSession</param>
|
||||
protected internal virtual void OnSessionCreate(ulong xrSession) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the reference xrSpace for the app changes.
|
||||
/// </summary>
|
||||
/// <param name="xrSpace">Handle of the xrSpace</param>
|
||||
protected internal virtual void OnAppSpaceChange(ulong xrSpace) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the OpenXR loader receives the XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED event
|
||||
/// from the runtime signaling that the XrSessionState has changed.
|
||||
/// </summary>
|
||||
/// <param name="oldState">Previous state</param>
|
||||
/// <param name="newState">New state</param>
|
||||
protected internal virtual void OnSessionStateChange(int oldState, int newState) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called after xrSessionBegin.
|
||||
/// </summary>
|
||||
/// <param name="xrSession">Handle of the xrSession</param>
|
||||
protected internal virtual void OnSessionBegin(ulong xrSession) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called before xrEndSession.
|
||||
/// </summary>
|
||||
/// <param name="xrSession">Handle of the xrSession</param>
|
||||
protected internal virtual void OnSessionEnd(ulong xrSession) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the runtime transitions to the XR_SESSION_STATE_EXITING state.
|
||||
/// </summary>
|
||||
/// <param name="xrSession">Handle of the xrSession</param>
|
||||
protected internal virtual void OnSessionExiting(ulong xrSession) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called before xrDestroySession.
|
||||
/// </summary>
|
||||
/// <param name="xrSession">Handle of the xrSession</param>
|
||||
protected internal virtual void OnSessionDestroy(ulong xrSession) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called before xrDestroyInstance
|
||||
/// </summary>
|
||||
/// <param name="xrInstance">Handle of the xrInstance</param>
|
||||
protected internal virtual void OnInstanceDestroy(ulong xrInstance) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the runtime transitions to the XR_SESSION_STATE_LOSS_PENDING
|
||||
/// state. This is a notification to the feature implementer that the session is
|
||||
/// about to be lost. This feature should do what it needs to do to
|
||||
/// prepare for potential session recreation.
|
||||
/// </summary>
|
||||
/// <param name="xrSession">The session that is going to be lost</param>
|
||||
protected internal virtual void OnSessionLossPending(ulong xrSession) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the OpenXR loader receives the XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event
|
||||
/// from the runtime. This is a notification to the feature implementer that the instance is
|
||||
/// about to be lost. This feature should do what it needs to do to
|
||||
/// clean up in preparation for termination.
|
||||
/// </summary>
|
||||
/// <param name="xrInstance">The instance that is going to be lost</param>
|
||||
protected internal virtual void OnInstanceLossPending(ulong xrInstance) { }
|
||||
|
||||
/// <summary>
|
||||
/// Notification to the feature implementer that the form factor has changed.
|
||||
/// </summary>
|
||||
/// <param name="xrFormFactor">New form factor value</param>
|
||||
protected internal virtual void OnFormFactorChange(int xrFormFactor) { }
|
||||
|
||||
/// <summary>
|
||||
/// Notification to the feature implementer that the view configuration type has changed.
|
||||
/// </summary>
|
||||
/// <param name="xrViewConfigurationType">New view configuration type</param>
|
||||
protected internal virtual void OnViewConfigurationTypeChange(int xrViewConfigurationType) { }
|
||||
|
||||
/// <summary>
|
||||
/// Notification to the feature implementer that the environment blend mode has changed.
|
||||
/// </summary>
|
||||
/// <param name="xrEnvironmentBlendMode">New environment blend mode value</param>
|
||||
protected internal virtual void OnEnvironmentBlendModeChange(XrEnvironmentBlendMode xrEnvironmentBlendMode) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the enabled state of a feature changes
|
||||
/// </summary>
|
||||
protected internal virtual void OnEnabledChange()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an XrPath to a string.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to convert</param>
|
||||
/// <returns>String that represents the path, or null if the path is invalid.</returns>
|
||||
protected static string PathToString(ulong path) =>
|
||||
Internal_PathToStringPtr(path, out var stringPtr) ? Marshal.PtrToStringAnsi(stringPtr) : null;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string to an XrPath.
|
||||
/// </summary>
|
||||
/// <param name="str">String to convert</param>
|
||||
/// <returns>Path of converted string, or XrPath.none if string could not be converted.</returns>
|
||||
protected static ulong StringToPath(string str) =>
|
||||
Internal_StringToPath(str, out var id) ? id : 0ul;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the path of the current interaction profile for the given user path.
|
||||
/// </summary>
|
||||
/// <param name="userPath">OpenXR User Path (eg: /user/hand/left)</param>
|
||||
/// <returns>A path to the interaction profile, or XrPath.none if the path could not be retrieved.</returns>
|
||||
protected static ulong GetCurrentInteractionProfile(ulong userPath) =>
|
||||
Internal_GetCurrentInteractionProfile(userPath, out ulong profileId) ? profileId : 0ul;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the path of the current interaction profile for the given user path.
|
||||
/// </summary>
|
||||
/// <param name="userPath">User path</param>
|
||||
/// <returns>A path to the interaction profile, or XrPath.none if the path could not be retrieved.</returns>
|
||||
protected static ulong GetCurrentInteractionProfile(string userPath) =>
|
||||
GetCurrentInteractionProfile(StringToPath(userPath));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current app space.
|
||||
/// </summary>
|
||||
/// <returns>Current app space</returns>
|
||||
protected static ulong GetCurrentAppSpace() =>
|
||||
Internal_GetAppSpace(out ulong appSpaceId) ? appSpaceId : 0ul;
|
||||
|
||||
/// <summary>
|
||||
/// Returns viewConfigurationType for the given renderPass index.
|
||||
/// </summary>
|
||||
/// <param name="renderPassIndex">RenderPass index</param>
|
||||
/// <returns>viewConfigurationType for certain renderPass. Return 0 if invalid renderPass.</returns>
|
||||
protected static int GetViewConfigurationTypeForRenderPass(int renderPassIndex) =>
|
||||
Internal_GetViewTypeFromRenderIndex(renderPassIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Set the current XR Environment Blend Mode if it is supported by the active runtime. If not supported, fall back to the runtime preference.
|
||||
/// </summary>
|
||||
/// <param name="xrEnvironmentBlendMode">Environment Blend Mode (e.g.: Opaque = 1, Additive = 2, AlphaBlend = 3)</param>
|
||||
protected static void SetEnvironmentBlendMode(XrEnvironmentBlendMode xrEnvironmentBlendMode) =>
|
||||
Internal_SetEnvironmentBlendMode(xrEnvironmentBlendMode);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current XR Environment Blend Mode.
|
||||
/// </summary>
|
||||
/// <returns>Current XR Environment Blend Mode</returns>
|
||||
protected static XrEnvironmentBlendMode GetEnvironmentBlendMode() =>
|
||||
Internal_GetEnvironmentBlendMode();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// A Build-time validation rule.
|
||||
/// </summary>
|
||||
public class ValidationRule
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a validation rule for an OpenXRFeature.
|
||||
/// </summary>
|
||||
/// <param name="feature">Feature to create validation rule for</param>
|
||||
public ValidationRule(OpenXRFeature feature)
|
||||
{
|
||||
if (feature == null)
|
||||
throw new Exception("Invalid feature");
|
||||
this.feature = feature;
|
||||
}
|
||||
|
||||
internal ValidationRule()
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Message describing the rule that will be showed to the developer if it fails.
|
||||
/// </summary>
|
||||
public string message;
|
||||
|
||||
/// <summary>
|
||||
/// Lambda function that returns true if validation passes, false if validation fails.
|
||||
/// </summary>
|
||||
public Func<bool> checkPredicate;
|
||||
|
||||
/// <summary>
|
||||
/// Lambda function that fixes the issue, if possible.
|
||||
/// </summary>
|
||||
public Action fixIt;
|
||||
|
||||
/// <summary>
|
||||
/// Text describing how the issue is fixed, shown in a tooltip.
|
||||
/// </summary>
|
||||
public string fixItMessage;
|
||||
|
||||
/// <summary>
|
||||
/// True if the fixIt Lambda function performs a function that is automatic and does not require user input. If your fixIt
|
||||
/// function requires user input, set fixitAutomatic to false to prevent the fixIt method from being executed during fixAll
|
||||
/// </summary>
|
||||
public bool fixItAutomatic = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, failing the rule is treated as an error and stops the build.
|
||||
/// If false, failing the rule is treated as a warning and it doesn't stop the build. The developer has the option to correct the problem, but is not required to.
|
||||
/// </summary>
|
||||
public bool error;
|
||||
|
||||
/// <summary>
|
||||
/// If true, will deny the project from entering playmode in editor.
|
||||
/// If false, can still enter playmode in editor if this issue isn't fixed.
|
||||
/// </summary>
|
||||
public bool errorEnteringPlaymode;
|
||||
|
||||
/// <summary>
|
||||
/// Optional text to display in a help icon with the issue in the validator.
|
||||
/// </summary>
|
||||
public string helpText;
|
||||
|
||||
/// <summary>
|
||||
/// Optional link that will be opened if the help icon is clicked.
|
||||
/// </summary>
|
||||
public string helpLink;
|
||||
|
||||
/// <summary>
|
||||
/// Optional struct HighlighterFocusData used to create HighlighterFocus functionality.
|
||||
/// WindowTitle contains the name of the window tab to highlight in.
|
||||
/// SearchPhrase contains the text to be searched and highlighted.
|
||||
/// </summary>
|
||||
public HighlighterFocusData highlighterFocus { get; set; }
|
||||
public struct HighlighterFocusData
|
||||
{
|
||||
public string windowTitle { get; set; }
|
||||
public string searchText { get; set; }
|
||||
}
|
||||
|
||||
internal OpenXRFeature feature;
|
||||
|
||||
internal BuildTargetGroup buildTargetGroup = BuildTargetGroup.Unknown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows a feature to add to a list of validation rules which your feature will evaluate at build time.
|
||||
/// Details of the validation results can be found in OpenXRProjectValidation.
|
||||
/// </summary>
|
||||
/// <param name="rules">Your feature will check the rules in this list at build time. Add rules that you want your feature to check, and remove rules that you want your feature to ignore.</param>
|
||||
/// <param name="targetGroup">Build target group these validation rules will be evaluated for.</param>
|
||||
protected internal virtual void GetValidationChecks(List<ValidationRule> rules, BuildTargetGroup targetGroup)
|
||||
{
|
||||
}
|
||||
|
||||
internal static void GetFullValidationList(List<ValidationRule> rules, BuildTargetGroup targetGroup)
|
||||
{
|
||||
var openXrSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup);
|
||||
if (openXrSettings == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var tempList = new List<ValidationRule>();
|
||||
foreach (var feature in openXrSettings.features)
|
||||
{
|
||||
if (feature != null)
|
||||
{
|
||||
feature.GetValidationChecks(tempList, targetGroup);
|
||||
rules.AddRange(tempList);
|
||||
tempList.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void GetValidationList(List<ValidationRule> rules, BuildTargetGroup targetGroup)
|
||||
{
|
||||
var openXrSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup);
|
||||
if (openXrSettings == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var features = openXrSettings.features.Where(f => f != null)
|
||||
.OrderByDescending(f => f.priority)
|
||||
.ThenBy(f => f.nameUi);
|
||||
foreach (var feature in features)
|
||||
{
|
||||
if (feature != null && feature.enabled)
|
||||
feature.GetValidationChecks(rules, targetGroup);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Creates a subsystem based on a given a list of descriptors and a specific subsystem id.
|
||||
/// Promoted to public for extensions.
|
||||
/// </summary>
|
||||
///
|
||||
/// <typeparam name="TDescriptor">The descriptor type being passed in</typeparam>
|
||||
/// <typeparam name="TSubsystem">The subsystem type being requested</typeparam>
|
||||
/// <param name="descriptors">List of TDescriptor instances to use for subsystem matching</param>
|
||||
/// <param name="id">The identifier key of the particular subsystem implementation being requested</param>
|
||||
protected void CreateSubsystem<TDescriptor, TSubsystem>(List<TDescriptor> descriptors, string id)
|
||||
where TDescriptor : ISubsystemDescriptor
|
||||
where TSubsystem : ISubsystem
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance == null)
|
||||
{
|
||||
Debug.LogError("CreateSubsystem called before loader was initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
OpenXRLoaderBase.Instance.CreateSubsystem<TDescriptor, TSubsystem>(descriptors, id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start a subsystem instance of a given type. Subsystem is assumed to already be loaded from
|
||||
/// a previous call to CreateSubsystem.
|
||||
/// Promoted to public for extensions.
|
||||
/// </summary>
|
||||
///
|
||||
/// <typeparam name="T">A subclass of <see cref="ISubsystem"/></typeparam>
|
||||
protected void StartSubsystem<T>() where T : class, ISubsystem
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance == null)
|
||||
{
|
||||
Debug.LogError("StartSubsystem called before loader was initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
OpenXRLoaderBase.Instance.StartSubsystem<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops a subsystem instance of a given type. Subsystem is assumed to already be loaded from
|
||||
/// a previous call to CreateSubsystem.
|
||||
/// Promoted to public for extensions.
|
||||
/// </summary>
|
||||
///
|
||||
/// <typeparam name="T">A subclass of <see cref="ISubsystem"/></typeparam>
|
||||
protected void StopSubsystem<T>() where T : class, ISubsystem
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance == null)
|
||||
{
|
||||
Debug.LogError("StopSubsystem called before loader was initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
OpenXRLoaderBase.Instance.StopSubsystem<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys a subsystem instance of a given type. Subsystem is assumed to already be loaded from
|
||||
/// a previous call to CreateSubsystem.
|
||||
/// Promoted to public for extensions.
|
||||
/// </summary>
|
||||
///
|
||||
/// <typeparam name="T">A subclass of <see cref="ISubsystem"/></typeparam>
|
||||
protected void DestroySubsystem<T>() where T : class, ISubsystem
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance == null)
|
||||
{
|
||||
Debug.LogError("DestroySubsystem called before loader was initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
OpenXRLoaderBase.Instance.DestroySubsystem<T>();
|
||||
}
|
||||
|
||||
/// <summary>Called when the object is loaded.</summary>
|
||||
/// <remarks>
|
||||
/// Additional information:
|
||||
/// <a href="https://docs.unity3d.com/ScriptReference/ScriptableObject.OnEnable.html">ScriptableObject.OnEnable</a>
|
||||
/// </remarks>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Called when the object is loaded.</summary>
|
||||
/// <remarks>
|
||||
/// Additional information:
|
||||
/// <a href="https://docs.unity3d.com/ScriptReference/ScriptableObject.OnDisable.html">ScriptableObject.OnDisable</a>
|
||||
/// </remarks>
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
// Virtual for future expansion and to match OnEnable
|
||||
}
|
||||
|
||||
/// <summary>Called when the object is loaded.</summary>
|
||||
/// <remarks>
|
||||
/// Additional information:
|
||||
/// <a href="https://docs.unity3d.com/ScriptReference/ScriptableObject.Awake.html">ScriptableObject.Awake</a>
|
||||
/// </remarks>
|
||||
protected virtual void Awake()
|
||||
{
|
||||
}
|
||||
|
||||
internal enum LoaderEvent
|
||||
{
|
||||
SubsystemCreate,
|
||||
SubsystemDestroy,
|
||||
SubsystemStart,
|
||||
SubsystemStop,
|
||||
}
|
||||
|
||||
internal static bool ReceiveLoaderEvent(OpenXRLoaderBase loader, LoaderEvent e)
|
||||
{
|
||||
var instance = OpenXRSettings.Instance;
|
||||
if (instance == null)
|
||||
return true;
|
||||
|
||||
foreach (var feature in instance.features)
|
||||
{
|
||||
if (feature == null || !feature.enabled)
|
||||
continue;
|
||||
|
||||
switch (e)
|
||||
{
|
||||
case LoaderEvent.SubsystemCreate:
|
||||
feature.OnSubsystemCreate();
|
||||
break;
|
||||
case LoaderEvent.SubsystemDestroy:
|
||||
feature.OnSubsystemDestroy();
|
||||
break;
|
||||
case LoaderEvent.SubsystemStart:
|
||||
feature.OnSubsystemStart();
|
||||
break;
|
||||
case LoaderEvent.SubsystemStop:
|
||||
feature.OnSubsystemStop();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(e), e, null);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Must be kept in sync with unity_type.h ScriptEvents
|
||||
internal enum NativeEvent
|
||||
{
|
||||
// Setup
|
||||
XrSetupConfigValues,
|
||||
XrSystemIdChanged,
|
||||
XrInstanceChanged,
|
||||
XrSessionChanged,
|
||||
XrBeginSession,
|
||||
|
||||
// Runtime
|
||||
XrSessionStateChanged,
|
||||
XrChangedSpaceApp,
|
||||
|
||||
// Shutdown
|
||||
XrEndSession,
|
||||
XrDestroySession,
|
||||
XrDestroyInstance,
|
||||
|
||||
// General Session Events
|
||||
XrIdle,
|
||||
XrReady,
|
||||
XrSynchronized,
|
||||
XrVisible,
|
||||
XrFocused,
|
||||
XrStopping,
|
||||
XrExiting,
|
||||
XrLossPending,
|
||||
XrInstanceLossPending,
|
||||
XrRestartRequested,
|
||||
XrRequestRestartLoop,
|
||||
XrRequestGetSystemLoop,
|
||||
};
|
||||
|
||||
internal static void ReceiveNativeEvent(NativeEvent e, ulong payload)
|
||||
{
|
||||
if (null == OpenXRSettings.Instance)
|
||||
return;
|
||||
|
||||
foreach (var feature in OpenXRSettings.Instance.features)
|
||||
{
|
||||
if (feature == null || !feature.enabled)
|
||||
continue;
|
||||
|
||||
switch (e)
|
||||
{
|
||||
case NativeEvent.XrSetupConfigValues:
|
||||
feature.OnFormFactorChange(Internal_GetFormFactor());
|
||||
feature.OnEnvironmentBlendModeChange(Internal_GetEnvironmentBlendMode());
|
||||
feature.OnViewConfigurationTypeChange(Internal_GetViewConfigurationType());
|
||||
break;
|
||||
|
||||
case NativeEvent.XrSystemIdChanged:
|
||||
feature.OnSystemChange(payload);
|
||||
break;
|
||||
case NativeEvent.XrInstanceChanged:
|
||||
feature.failedInitialization = !feature.OnInstanceCreate(payload);
|
||||
requiredFeatureFailed |= (feature.required && feature.failedInitialization);
|
||||
break;
|
||||
case NativeEvent.XrSessionChanged:
|
||||
feature.OnSessionCreate(payload);
|
||||
break;
|
||||
case NativeEvent.XrBeginSession:
|
||||
feature.OnSessionBegin(payload);
|
||||
break;
|
||||
case NativeEvent.XrChangedSpaceApp:
|
||||
feature.OnAppSpaceChange(payload);
|
||||
break;
|
||||
case NativeEvent.XrSessionStateChanged:
|
||||
Internal_GetSessionState(out var oldState, out var newState);
|
||||
feature.OnSessionStateChange(oldState, newState);
|
||||
break;
|
||||
case NativeEvent.XrEndSession:
|
||||
feature.OnSessionEnd(payload);
|
||||
break;
|
||||
case NativeEvent.XrExiting:
|
||||
feature.OnSessionExiting(payload);
|
||||
break;
|
||||
case NativeEvent.XrDestroySession:
|
||||
feature.OnSessionDestroy(payload);
|
||||
break;
|
||||
case NativeEvent.XrDestroyInstance:
|
||||
feature.OnInstanceDestroy(payload);
|
||||
break;
|
||||
case NativeEvent.XrLossPending:
|
||||
feature.OnSessionLossPending(payload);
|
||||
break;
|
||||
case NativeEvent.XrInstanceLossPending:
|
||||
feature.OnInstanceLossPending(payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
requiredFeatureFailed = false;
|
||||
|
||||
var instance = OpenXRSettings.Instance;
|
||||
if (instance == null || instance.features == null)
|
||||
return;
|
||||
|
||||
foreach (var feature in instance.features)
|
||||
if (feature != null)
|
||||
feature.failedInitialization = false;
|
||||
}
|
||||
|
||||
internal static void HookGetInstanceProcAddr()
|
||||
{
|
||||
var procAddr = Internal_GetProcAddressPtr(true);
|
||||
|
||||
var instance = OpenXRSettings.Instance;
|
||||
if (instance != null && instance.features != null)
|
||||
{
|
||||
// Hook the features in reverse priority order to ensure the highest priority feature is
|
||||
// hooked last. This will ensure the highest priority feature is called first in the chain.
|
||||
for (var featureIndex = instance.features.Length - 1; featureIndex >= 0; featureIndex--)
|
||||
{
|
||||
var feature = instance.features[featureIndex];
|
||||
if (feature == null || !feature.enabled)
|
||||
continue;
|
||||
|
||||
procAddr = feature.HookGetInstanceProcAddr(procAddr);
|
||||
}
|
||||
}
|
||||
|
||||
Internal_SetProcAddressPtrAndLoadStage1(procAddr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns XrAction handle bound to the given <see cref="UnityEngine.InputSystem.InputAction"/>.
|
||||
/// </summary>
|
||||
/// <param name="inputAction">Action to retrieve XrAction handles for</param>
|
||||
/// <returns>XrAction handle bound to the given <see cref="UnityEngine.InputSystem.InputAction"/> or 0 if there is no bound XrAction</returns>
|
||||
protected ulong GetAction(InputAction inputAction) => OpenXRInput.GetActionHandle(inputAction);
|
||||
|
||||
/// <summary>
|
||||
/// Returns XrAction handle bound to the given device and usage.
|
||||
/// </summary>
|
||||
/// <param name="device">Device to retrieve XrAction handles for</param>
|
||||
/// <param name="usage">Usage to retrieve XrAction handles for</param>
|
||||
/// <returns>XrAction handle bound to the given device and usage, or 0 if there is no bound XrAction</returns>
|
||||
protected ulong GetAction(InputDevice device, InputFeatureUsage usage) => OpenXRInput.GetActionHandle(device, usage);
|
||||
|
||||
/// <summary>
|
||||
/// Returns XrAction handle bound to the given device and usage.
|
||||
/// </summary>
|
||||
/// <param name="device">Device to retrieve XrAction handles for</param>
|
||||
/// <param name="usageName">Usage name to retrieve XrAction handles for</param>
|
||||
/// <returns>XrAction handle bound to the given device and usage, or 0 if there is no bound XrAction</returns>
|
||||
protected ulong GetAction(InputDevice device, string usageName) => OpenXRInput.GetActionHandle(device, usageName);
|
||||
|
||||
/// <summary>
|
||||
/// Flags that control various options and behaviors on registered stats.
|
||||
/// </summary>
|
||||
[System.Flags]
|
||||
protected internal enum StatFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Stat will have no special options or behaviors
|
||||
/// </summary>
|
||||
StatOptionNone = 0,
|
||||
/// <summary>
|
||||
/// Stat will clear to 0.0f at the beginning of every frame
|
||||
/// </summary>
|
||||
ClearOnUpdate = 1 << 0,
|
||||
/// <summary>
|
||||
/// Stat will have all special options and behaviors
|
||||
/// </summary>
|
||||
All = (1 << 1) - 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an OpenXR statistic with the given name and flags.
|
||||
/// This method is not thread safe, so it should only be called at OnInstanceCreate.
|
||||
/// </summary>
|
||||
/// <param name="statName">String identifier for the statistic.</param>
|
||||
/// <param name="statFlags">Properties to be applied to the statistic.</param>
|
||||
/// <returns>Stat Id</returns>
|
||||
protected internal static ulong RegisterStatsDescriptor(string statName, StatFlags statFlags)
|
||||
{
|
||||
return runtime_RegisterStatsDescriptor(statName, statFlags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a float value to a registered statistic. Its thread safe.
|
||||
/// </summary>
|
||||
/// <param name="statId">Identifier of the previously registered statistic.</param>
|
||||
/// <param name="value">Float value to be assigned to the stat.</param>
|
||||
protected internal static void SetStatAsFloat(ulong statId, float value)
|
||||
{
|
||||
runtime_SetStatAsFloat(statId, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns an unsigned integer value to a registered statistic. It is thread safe.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// IMPORTANT: Due to limitations in native code, values over 16777216 (1<<24) might not be reflected accurately.
|
||||
/// </remarks>
|
||||
/// <param name="statId">Identifier of the previously registered statistic.</param>
|
||||
/// <param name="value">Unsigned integer value to be assigned to the stat.</param>
|
||||
protected internal static void SetStatAsUInt(ulong statId, uint value)
|
||||
{
|
||||
runtime_SetStatAsUInt(statId, value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a921ca457a309d94c96e7d15a0df0554
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
|
||||
#if UNITY_EDITOR || PACKAGE_DOCS_GENERATION
|
||||
namespace UnityEditor.XR.OpenXR.Features
|
||||
{
|
||||
public class FeatureCategory
|
||||
{
|
||||
public const string Default = "";
|
||||
public const string Feature = "Feature";
|
||||
public const string Interaction = "Interaction";
|
||||
}
|
||||
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class OpenXRFeatureAttribute : Attribute
|
||||
{
|
||||
internal class CopyFieldAttribute : Attribute
|
||||
{
|
||||
public string FieldName;
|
||||
public CopyFieldAttribute(string fieldName)
|
||||
{
|
||||
FieldName = fieldName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Feature name to show in the feature configuration UI.
|
||||
/// </summary>
|
||||
[CopyField(nameof(OpenXRFeature.nameUi))] public string UiName;
|
||||
|
||||
/// <summary>
|
||||
/// Hide this feature from the UI.
|
||||
/// </summary>
|
||||
public bool Hidden;
|
||||
|
||||
/// <summary>
|
||||
/// Feature description to show in the UI.
|
||||
/// </summary>
|
||||
public string Desc;
|
||||
|
||||
/// <summary>
|
||||
/// OpenXR runtime extension strings that need to be enabled to use this extension.
|
||||
/// If these extensions can't be enabled, a message will be logged, but execution will continue.
|
||||
/// Can contain multiple extensions separated by spaces.
|
||||
/// </summary>
|
||||
[CopyField(nameof(OpenXRFeature.openxrExtensionStrings))] public string OpenxrExtensionStrings;
|
||||
|
||||
/// <summary>
|
||||
/// Company that created the feature, shown in the feature configuration UI.
|
||||
/// </summary>
|
||||
[CopyField(nameof(OpenXRFeature.company))] public string Company;
|
||||
|
||||
/// <summary>
|
||||
/// Link to the feature documentation. The help button in the UI opens this link in a web browser.
|
||||
/// </summary>
|
||||
public string DocumentationLink;
|
||||
|
||||
/// <summary>
|
||||
/// Feature version.
|
||||
/// </summary>
|
||||
[CopyField(nameof(OpenXRFeature.version))] public string Version;
|
||||
|
||||
/// <summary>
|
||||
/// BuildTargets in this list use a custom runtime loader (that is, openxr_loader.dll).
|
||||
/// Only one feature per platform can have a custom runtime loader.
|
||||
/// Unity will skip copying the default loader to the build and use this feature's loader instead on these platforms.
|
||||
/// Loader must be placed alongside the OpenXRFeature script or in a subfolder of it.
|
||||
/// </summary>
|
||||
public BuildTarget[] CustomRuntimeLoaderBuildTargets;
|
||||
|
||||
/// <summary>
|
||||
/// BuildTargetsGroups that this feature supports. The feature will only be shown or included on these platforms.
|
||||
/// </summary>
|
||||
public BuildTargetGroup[] BuildTargetGroups;
|
||||
|
||||
/// <summary>
|
||||
/// Feature category.
|
||||
/// </summary>
|
||||
public string Category = "";
|
||||
|
||||
/// <summary>
|
||||
/// True if this feature is required, false otherwise.
|
||||
/// Required features will cause the loader to fail to initialize if they fail to initialize or start.
|
||||
/// </summary>
|
||||
[CopyField(nameof(OpenXRFeature.required))] public bool Required = false;
|
||||
|
||||
/// <summary>
|
||||
/// Determines the order in which the feature will be called in both the GetInstanceProcAddr hook list and
|
||||
/// when events such as OnInstanceCreate are called. Higher priority features will hook after lower priority features and
|
||||
/// be called first in the event list.
|
||||
/// </summary>
|
||||
[CopyField(nameof(OpenXRFeature.priority))] public int Priority = 0;
|
||||
|
||||
/// <summary>
|
||||
/// A well known string id for this feature. It is recommended that that id be in reverse DNS naming format (com.foo.bar.feature).
|
||||
/// </summary>
|
||||
[CopyField(nameof(OpenXRFeature.featureIdInternal))] public string FeatureId = "";
|
||||
|
||||
|
||||
internal static readonly System.Text.RegularExpressions.Regex k_PackageVersionRegex = new System.Text.RegularExpressions.Regex(@"(\d*\.\d*)\..*");
|
||||
|
||||
/// <summary>
|
||||
/// This method returns the OpenXR internal documentation link. This is necessary because the documentation link was made public in the
|
||||
/// Costants class which prevents it from being alterned in anything but a major revision. This method will patch up the documentation links
|
||||
/// as needed as long as they are internal openxr documentation links.
|
||||
/// </summary>
|
||||
internal string InternalDocumentationLink
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(DocumentationLink))
|
||||
return DocumentationLink;
|
||||
|
||||
// Update the version if needed
|
||||
if (DocumentationLink.StartsWith(Constants.k_DocumentationManualURL))
|
||||
{
|
||||
var version = PackageManager.PackageInfo.FindForAssembly(typeof(OpenXRFeatureAttribute).Assembly)?.version;
|
||||
var majorminor = k_PackageVersionRegex.Match(version).Groups[1].Value;
|
||||
DocumentationLink = DocumentationLink.Replace("1.0", majorminor);
|
||||
}
|
||||
|
||||
return DocumentationLink;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f8c88249c4adc844ba1a147fcaf83e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features
|
||||
{
|
||||
public abstract partial class OpenXRFeature
|
||||
{
|
||||
const string Library = "UnityOpenXR";
|
||||
|
||||
[DllImport(Library, EntryPoint = "Internal_PathToString")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
static extern bool Internal_PathToStringPtr(ulong pathId, out IntPtr path);
|
||||
|
||||
[DllImport(Library, EntryPoint = "Internal_StringToPath")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
static extern bool Internal_StringToPath([MarshalAs(UnmanagedType.LPStr)] string str, out ulong pathId);
|
||||
|
||||
[DllImport(Library, EntryPoint = "Internal_GetCurrentInteractionProfile")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
static extern bool Internal_GetCurrentInteractionProfile(ulong pathId, out ulong interactionProfile);
|
||||
|
||||
[DllImport(Library, EntryPoint = "NativeConfig_GetFormFactor")]
|
||||
static extern int Internal_GetFormFactor();
|
||||
|
||||
[DllImport(Library, EntryPoint = "NativeConfig_GetViewConfigurationType")]
|
||||
static extern int Internal_GetViewConfigurationType();
|
||||
|
||||
[DllImport(Library, EntryPoint = "NativeConfig_GetViewTypeFromRenderIndex")]
|
||||
static extern int Internal_GetViewTypeFromRenderIndex(int renderPassIndex);
|
||||
|
||||
[DllImport(Library, EntryPoint = "OpenXRInputProvider_GetXRSession")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool Internal_GetXRSession(out ulong xrSession);
|
||||
|
||||
[DllImport(Library, EntryPoint = "session_GetSessionState")]
|
||||
static extern void Internal_GetSessionState(out int oldState, out int newState);
|
||||
|
||||
[DllImport(Library, EntryPoint = "NativeConfig_GetEnvironmentBlendMode")]
|
||||
static extern XrEnvironmentBlendMode Internal_GetEnvironmentBlendMode();
|
||||
|
||||
[DllImport(Library, EntryPoint = "NativeConfig_SetEnvironmentBlendMode")]
|
||||
static extern void Internal_SetEnvironmentBlendMode(XrEnvironmentBlendMode xrEnvironmentBlendMode);
|
||||
|
||||
[DllImport(Library, EntryPoint = "OpenXRInputProvider_GetAppSpace")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool Internal_GetAppSpace(out ulong appSpace);
|
||||
|
||||
[DllImport(Library, EntryPoint = "NativeConfig_GetProcAddressPtr")]
|
||||
internal static extern IntPtr Internal_GetProcAddressPtr([MarshalAs(UnmanagedType.I1)] bool loaderDefault);
|
||||
|
||||
[DllImport(Library, EntryPoint = "NativeConfig_SetProcAddressPtrAndLoadStage1")]
|
||||
internal static extern void Internal_SetProcAddressPtrAndLoadStage1(IntPtr func);
|
||||
|
||||
[DllImport(Library)]
|
||||
internal static extern ulong runtime_RegisterStatsDescriptor(string statName, StatFlags statFlags);
|
||||
|
||||
[DllImport(Library)]
|
||||
internal static extern void runtime_SetStatAsFloat(ulong statId, float value);
|
||||
|
||||
[DllImport(Library)]
|
||||
internal static extern void runtime_SetStatAsUInt(ulong statId, uint value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1205eddf78bff947b6e9f98809dbe33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
[assembly: InternalsVisibleTo("UnityEditor.XR.OpenXR.Tests")]
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
public partial class OpenXRSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// All known features.
|
||||
/// </summary>
|
||||
[FormerlySerializedAs("extensions")]
|
||||
[HideInInspector]
|
||||
[SerializeField]
|
||||
internal OpenXRFeature[] features = { };
|
||||
|
||||
/// <summary>
|
||||
/// Number of available features.
|
||||
/// </summary>
|
||||
public int featureCount => features.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first feature that matches the given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFeature">Type of the feature to retrieve</typeparam>
|
||||
/// <returns>Feature by type</returns>
|
||||
public TFeature GetFeature<TFeature>() where TFeature : OpenXRFeature => (TFeature)GetFeature(typeof(TFeature));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first feature that matches the given type.
|
||||
/// </summary>
|
||||
/// <param name="featureType">Type of the feature to return</param>
|
||||
/// <returns>Feature by type</returns>
|
||||
public OpenXRFeature GetFeature(Type featureType)
|
||||
{
|
||||
foreach (var feature in features)
|
||||
if (featureType.IsInstanceOfType(feature))
|
||||
return feature;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all features of a given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFeature">Type of the feature to retrieve</typeparam>
|
||||
/// <returns>All components of Type</returns>
|
||||
public OpenXRFeature[] GetFeatures<TFeature>() => GetFeatures(typeof(TFeature));
|
||||
|
||||
/// <summary>
|
||||
/// Returns all features of Type.
|
||||
/// </summary>
|
||||
/// <param name="featureType">Type of the feature to retrieve</param>
|
||||
/// <returns>All components of Type</returns>
|
||||
public OpenXRFeature[] GetFeatures(Type featureType)
|
||||
{
|
||||
var result = new List<OpenXRFeature>();
|
||||
foreach (var feature in features)
|
||||
if (featureType.IsInstanceOfType(feature))
|
||||
result.Add(feature);
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all features of a given type.
|
||||
/// </summary>
|
||||
/// <param name="featuresOut">Output list of features</param>
|
||||
/// <typeparam name="TFeature">Feature type</typeparam>
|
||||
/// <returns>Number of features returned</returns>
|
||||
public int GetFeatures<TFeature>(List<TFeature> featuresOut) where TFeature : OpenXRFeature
|
||||
{
|
||||
featuresOut.Clear();
|
||||
foreach (var feature in features)
|
||||
if (feature is TFeature xrFeature)
|
||||
featuresOut.Add(xrFeature);
|
||||
|
||||
return featuresOut.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all features of a given type.
|
||||
/// </summary>
|
||||
/// <param name="featureType">Type of the feature to retrieve</param>
|
||||
/// <param name="featuresOut">Output list of features</param>
|
||||
/// <returns>Number of features returned</returns>
|
||||
public int GetFeatures(Type featureType, List<OpenXRFeature> featuresOut)
|
||||
{
|
||||
featuresOut.Clear();
|
||||
foreach (var feature in features)
|
||||
if (featureType.IsInstanceOfType(feature))
|
||||
featuresOut.Add(feature);
|
||||
|
||||
return featuresOut.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return all features.
|
||||
/// </summary>
|
||||
/// <returns>All features</returns>
|
||||
public OpenXRFeature[] GetFeatures() => (OpenXRFeature[])features?.Clone() ?? new OpenXRFeature[0];
|
||||
|
||||
/// <summary>
|
||||
/// Return all features.
|
||||
/// </summary>
|
||||
/// <param name="featuresOut">Output list of features</param>
|
||||
/// <returns>Number of features returned</returns>
|
||||
public int GetFeatures(List<OpenXRFeature> featuresOut)
|
||||
{
|
||||
featuresOut.Clear();
|
||||
featuresOut.AddRange(features);
|
||||
return featuresOut.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07304509a07b28c42b25d3afb271331a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,417 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if UNITY_EDITOR
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.Management;
|
||||
using UnityEngine.XR.Management;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// A Unity OpenXR Interaction feature.
|
||||
/// This class can be inherited from to add a custom action mapping for OpenXR.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class OpenXRInteractionFeature : OpenXRFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// Temporary static list used for action map creation
|
||||
/// </summary>
|
||||
private static List<ActionMapConfig> m_CreatedActionMaps = null;
|
||||
private static Dictionary<InteractionProfileType, Dictionary<string, bool>> m_InteractionProfileEnabledMaps = new Dictionary<InteractionProfileType, Dictionary<string, bool>>();
|
||||
|
||||
/// <summary>
|
||||
/// Flag that indicates this feature or profile is additive and its binding paths will be added to other non-additive profiles if enabled.
|
||||
/// </summary>
|
||||
internal virtual bool IsAdditive => false;
|
||||
|
||||
/// <summary>
|
||||
/// The underlying type of an OpenXR action. This enumeration contains all supported control types within OpenXR. This is used when declaring actions in OpenXR with XrAction/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
protected internal enum ActionType
|
||||
{
|
||||
/// <summary>A binary (on/off) action type. Represented by ButtonControl in the Input System or Boolean in XR.InputDevice.</summary>
|
||||
Binary,
|
||||
/// <summary>A single Axis float action type. Represented by an AxisControl in the InputSystem or a float in XR.InputDevice.</summary>
|
||||
Axis1D,
|
||||
/// <summary>A two-dimensional float action type. Represented by a Vector2Control in the InputSystem or Vector2 in XR.InputDevice.</summary>
|
||||
Axis2D,
|
||||
/// <summary>A position and rotation in three-dimensional space. Represented by a PoseControl in the InputSystem, and a series of controls (boolean to represent if it's being tracked or not, unsigned integer for which fields are available, Vector3 for position, Quaternion for rotation) in XR.InputDevice.</summary>
|
||||
Pose,
|
||||
/// <summary>This control represents an output motor. Usable as sequential channels (first declared is channel 0, second is 1, etc...) in both the Input System and XR.InputDevice haptic APIs.</summary>
|
||||
Vibrate,
|
||||
/// <summary>A value representing the total number of ActionTypes available. This can be used to check if an ActionType value is a valid ActionType.</summary>
|
||||
Count
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information sent to OpenXR about specific, physical control on an input device. Used to identify what an action is bound to (that is, which physical control will trigger that action).
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
protected internal class ActionBinding
|
||||
{
|
||||
/// <summary>OpenXR interaction profile name</summary>
|
||||
public string interactionProfileName;
|
||||
|
||||
/// <summary>OpenXR path for the interaction</summary>
|
||||
public string interactionPath;
|
||||
|
||||
/// <summary>Optional OpenXR user paths <see cref="UserPaths"/></summary>
|
||||
public List<string> userPaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declares an abstract input source bound to multiple physical input controls. XrActions are commonly contained within an ActionMapConfig as a grouped series of abstract, remappable inputs.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
protected internal class ActionConfig
|
||||
{
|
||||
/// <summary>The name of the action, reported into the InputSystem as the name of the control that represents the input data for this action. This name can only contain a-z lower case letters.</summary>
|
||||
public string name;
|
||||
|
||||
/// <summary>The type of data this action will report. <see cref="ActionType"/></summary>
|
||||
public ActionType type;
|
||||
|
||||
/// <summary>Human readable name for the action</summary>
|
||||
public string localizedName;
|
||||
|
||||
/// <summary>The underlying physical input controls to use as the value for this action</summary>
|
||||
public List<ActionBinding> bindings;
|
||||
|
||||
/// <summary>These will be tagged onto <see cref="UnityEngine.XR.InputDevice"/> features. See <see cref="UnityEngine.XR.InputDevice.TryGetFeatureValue"/></summary>
|
||||
public List<string> usages;
|
||||
|
||||
/// <summary>Tag to determine if certain action is additive and could be added to the existing profiles</summary>
|
||||
public bool isAdditive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information sent to the OpenXR runtime to identify how to map a <see cref="UnityEngine.XR.InputDevice"/> or <see cref="UnityEngine.InputSystem.InputDevice"/> to an underlying OpenXR user path.
|
||||
/// </summary>
|
||||
protected internal class DeviceConfig
|
||||
{
|
||||
/// <summary>The <see cref="InputDeviceCharacteristics"/> for the <see cref="UnityEngine.XR.InputDevice"/> that will represent this ActionMapConfig. See <see cref="UnityEngine.XR.InputDevice.characteristics"/></summary>
|
||||
public InputDeviceCharacteristics characteristics;
|
||||
|
||||
/// <summary>OpenXR user path that this device maps to. <see cref="UserPaths"/></summary>
|
||||
public string userPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a mapping of between the Unity input system and OpenXR.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
protected internal class ActionMapConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the action map
|
||||
/// </summary>
|
||||
public string name;
|
||||
|
||||
/// <summary>
|
||||
/// Human readable name of the OpenXR user path that this device maps to.
|
||||
/// </summary>
|
||||
public string localizedName;
|
||||
|
||||
/// <summary>
|
||||
/// List of devices to configure
|
||||
/// </summary>
|
||||
public List<DeviceConfig> deviceInfos;
|
||||
|
||||
/// <summary>
|
||||
/// List of actions to configure
|
||||
/// </summary>
|
||||
public List<ActionConfig> actions;
|
||||
|
||||
/// <summary>
|
||||
/// OpenXR interaction profile to use for this action map
|
||||
/// </summary>
|
||||
public string desiredInteractionProfile;
|
||||
|
||||
/// <summary>
|
||||
/// Name of the manufacturer providing this action map
|
||||
/// </summary>
|
||||
public string manufacturer;
|
||||
|
||||
/// <summary>
|
||||
/// Serial number of the device
|
||||
/// </summary>
|
||||
public string serialNumber;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Common OpenXR user path definitions.
|
||||
/// See the [OpenXR Specification](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-user) for more information.
|
||||
/// </summary>
|
||||
public static class UserPaths
|
||||
{
|
||||
/// <summary>
|
||||
/// Path for user left hand
|
||||
/// </summary>
|
||||
public const string leftHand = "/user/hand/left";
|
||||
|
||||
/// <summary>
|
||||
/// Path for user right hand
|
||||
/// </summary>
|
||||
public const string rightHand = "/user/hand/right";
|
||||
|
||||
/// <summary>
|
||||
/// Path for user head
|
||||
/// </summary>
|
||||
public const string head = "/user/head";
|
||||
|
||||
/// <summary>
|
||||
/// Path for user gamepad
|
||||
/// </summary>
|
||||
public const string gamepad = "/user/gamepad";
|
||||
|
||||
/// <summary>
|
||||
/// Path for user treadmill
|
||||
/// </summary>
|
||||
public const string treadmill = "/user/treadmill";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flags used to indicate Interaction profile type
|
||||
/// </summary>
|
||||
public enum InteractionProfileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction profile derived from InputDevice class
|
||||
/// </summary>
|
||||
Device,
|
||||
/// <summary>
|
||||
/// Interaction profile derived from XRController class
|
||||
/// </summary>
|
||||
XRController
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a device layout with the Unity Input System.
|
||||
/// Called whenever this interaction profile is enabled in the Editor.
|
||||
/// </summary>
|
||||
protected virtual void RegisterDeviceLayout()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a device layout from the Unity Input System.
|
||||
/// Called whenever this interaction profile is disabled in the Editor.
|
||||
/// </summary>
|
||||
protected virtual void UnregisterDeviceLayout()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register action maps for this device with the OpenXR Runtime.
|
||||
/// Called at runtime before Start.
|
||||
/// </summary>
|
||||
protected virtual void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override bool OnInstanceCreate(ulong xrSession)
|
||||
{
|
||||
RegisterDeviceLayout();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return interaction profile type. Default type is XRController. Override this if interactionProfile is not derived from XRController class.
|
||||
/// </summary>
|
||||
/// <returns>Interaction profile type.</returns>
|
||||
protected virtual InteractionProfileType GetInteractionProfileType() => InteractionProfileType.XRController;
|
||||
|
||||
/// <summary>
|
||||
/// Return device layout name string used for register layouts in inputSystem.
|
||||
/// </summary>
|
||||
/// <returns>Device layout string.</returns>
|
||||
protected virtual string GetDeviceLayoutName() => "";
|
||||
|
||||
/// <summary>
|
||||
/// Request the feature create its action maps
|
||||
/// </summary>
|
||||
/// <param name="configs">Target list for the action maps</param>
|
||||
internal void CreateActionMaps(List<ActionMapConfig> configs)
|
||||
{
|
||||
m_CreatedActionMaps = configs;
|
||||
RegisterActionMapsWithRuntime();
|
||||
m_CreatedActionMaps = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an action map to the Unity Input System.
|
||||
///
|
||||
/// This method must be called from within the RegisterActionMapsWithRuntime method.
|
||||
/// </summary>
|
||||
/// <param name="map">Action map to add</param>
|
||||
protected void AddActionMap(ActionMapConfig map)
|
||||
{
|
||||
if (null == map)
|
||||
throw new ArgumentNullException("map");
|
||||
|
||||
if (null == m_CreatedActionMaps)
|
||||
throw new InvalidOperationException("ActionMap must be added from within the RegisterActionMapsWithRuntime method");
|
||||
|
||||
m_CreatedActionMaps.Add(map);
|
||||
}
|
||||
|
||||
internal virtual void AddAdditiveActions(List<OpenXRInteractionFeature.ActionMapConfig> actionMaps, ActionMapConfig additiveMap)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle enabled state change
|
||||
/// </summary>
|
||||
protected internal override void OnEnabledChange()
|
||||
{
|
||||
base.OnEnabledChange();
|
||||
#if UNITY_EDITOR && INPUT_SYSTEM_BINDING_VALIDATOR
|
||||
var packageSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
|
||||
if (null == packageSettings)
|
||||
return;
|
||||
|
||||
foreach (var feature in packageSettings.GetFeatures<OpenXRInteractionFeature>())
|
||||
{
|
||||
var profileType = ((OpenXRInteractionFeature) feature).GetInteractionProfileType();
|
||||
string deviceLayoutName = ((OpenXRInteractionFeature) feature).GetDeviceLayoutName();
|
||||
deviceLayoutName = "<" + deviceLayoutName + ">";
|
||||
if (m_InteractionProfileEnabledMaps.ContainsKey(profileType) && m_InteractionProfileEnabledMaps[profileType].ContainsKey(deviceLayoutName))
|
||||
m_InteractionProfileEnabledMaps[profileType][deviceLayoutName] = feature.enabled;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void RegisterLayouts()
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
#if UNITY_EDITOR
|
||||
var packageSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
|
||||
if (null == packageSettings)
|
||||
return;
|
||||
#if INPUT_SYSTEM_BINDING_VALIDATOR
|
||||
m_InteractionProfileEnabledMaps.Clear();
|
||||
foreach (var feature in packageSettings.GetFeatures<OpenXRInteractionFeature>())
|
||||
{
|
||||
//Register all the available profiles
|
||||
((OpenXRInteractionFeature) feature).RegisterDeviceLayout();
|
||||
|
||||
var profileType = ((OpenXRInteractionFeature) feature).GetInteractionProfileType();
|
||||
string deviceLayoutName = ((OpenXRInteractionFeature) feature).GetDeviceLayoutName();
|
||||
if (String.IsNullOrEmpty(deviceLayoutName))
|
||||
{
|
||||
Debug.LogWarningFormat("No GetDeviceLayoutName() override detected in {0}. Binding path validator for this interaction profile is not as effective. To fix, add GetDeviceLayoutName and GetInteractionProfileType override in this profile.", feature.nameUi);
|
||||
continue;
|
||||
}
|
||||
deviceLayoutName = "<" + deviceLayoutName + ">";
|
||||
if (!m_InteractionProfileEnabledMaps.ContainsKey(profileType))
|
||||
m_InteractionProfileEnabledMaps[profileType] = new Dictionary<string, bool>();
|
||||
|
||||
m_InteractionProfileEnabledMaps[profileType].Add(deviceLayoutName, feature.enabled);
|
||||
}
|
||||
InputSystem.InputSystem.customBindingPathValidators -= PathValidator;
|
||||
InputSystem.InputSystem.customBindingPathValidators += PathValidator;
|
||||
#else //#if INPUT_SYSTEM_BINDING_VALIDATOR
|
||||
foreach (var feature in packageSettings.GetFeatures<OpenXRInteractionFeature>())
|
||||
{
|
||||
//Register all the available profiles
|
||||
((OpenXRInteractionFeature)feature).RegisterDeviceLayout();
|
||||
}
|
||||
#endif //#if INPUT_SYSTEM_BINDING_VALIDATOR
|
||||
#else
|
||||
foreach (var feature in OpenXRSettings.Instance.GetFeatures<OpenXRInteractionFeature>())
|
||||
if (feature.enabled)
|
||||
((OpenXRInteractionFeature)feature).RegisterDeviceLayout();
|
||||
#endif //#if UNITY_EDITOR
|
||||
#endif //#if ENABLE_INPUT_SYSTEM
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR && INPUT_SYSTEM_BINDING_VALIDATOR
|
||||
internal static Action PathValidator(string bindingPath)
|
||||
{
|
||||
//case1: OpenXR plugin not enabled in XR management
|
||||
if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup))
|
||||
return null;
|
||||
|
||||
string warningText = null;
|
||||
//case2: current bindingPath maps to XRController.
|
||||
if (bindingPath.StartsWith("<XRController>"))
|
||||
{
|
||||
if (!m_InteractionProfileEnabledMaps.ContainsKey(InteractionProfileType.XRController))
|
||||
return null;
|
||||
bool controllerProfileEnabled = false;
|
||||
foreach (var profile in m_InteractionProfileEnabledMaps[InteractionProfileType.XRController])
|
||||
{
|
||||
if (profile.Value)
|
||||
controllerProfileEnabled = true;
|
||||
}
|
||||
if (controllerProfileEnabled)
|
||||
return null;
|
||||
warningText = "This binding will be inactive because there are no enabled OpenXR interaction profiles.";
|
||||
}
|
||||
else
|
||||
{
|
||||
//case3: current bindingPath maps to specific OpenXR interaction profile
|
||||
//Only check for bindings that belongs to OpenXRInteractionFeature
|
||||
bool checkXRInteractionBinding = false;
|
||||
bool profileEnabled = false;
|
||||
foreach (var map in m_InteractionProfileEnabledMaps)
|
||||
{
|
||||
foreach (var profile in map.Value)
|
||||
{
|
||||
if (bindingPath.StartsWith(profile.Key, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
checkXRInteractionBinding = true;
|
||||
profileEnabled = profile.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (checkXRInteractionBinding)
|
||||
break;
|
||||
}
|
||||
if (!checkXRInteractionBinding || profileEnabled)
|
||||
return null;
|
||||
|
||||
warningText = "This binding will be inactive because it refers to a disabled OpenXR interaction profile.";
|
||||
}
|
||||
// Draw the warning information in the Binding Properties panel
|
||||
return () =>
|
||||
{
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Label(EditorGUIUtility.IconContent("Warning@2x"), new GUIStyle(EditorStyles.label));
|
||||
GUILayout.Label(warningText, EditorStyles.wordWrappedLabel);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if(GUILayout.Button("Manage Interaction Profiles"))
|
||||
SettingsService.OpenProjectSettings("Project/XR Plug-in Management/OpenXR");
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.EndVertical();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal static bool OpenXRLoaderEnabledForSelectedBuildTarget(BuildTargetGroup targetGroup)
|
||||
{
|
||||
var settings =XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(targetGroup)?.AssignedSettings;
|
||||
if (!settings)
|
||||
return false;
|
||||
bool loaderFound = false;
|
||||
foreach (var activeLoader in settings.activeLoaders)
|
||||
{
|
||||
if (activeLoader as OpenXRLoader != null)
|
||||
{
|
||||
loaderFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return loaderFound;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08f06941bba688d4186b3d0d58406195
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
146
Packages/com.unity.xr.openxr/Runtime/OpenXRAnalytics.cs
Normal file
146
Packages/com.unity.xr.openxr/Runtime/OpenXRAnalytics.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if UNITY_ANALYTICS && ENABLE_CLOUD_SERVICES_ANALYTICS
|
||||
using UnityEngine.Analytics;
|
||||
#endif //UNITY_ANALYTICS && ENABLE_CLOUD_SERVICES_ANALYTICS
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
internal static class OpenXRAnalytics
|
||||
{
|
||||
private const int kMaxEventsPerHour = 1000;
|
||||
private const int kMaxNumberOfElements = 1000;
|
||||
private const string kVendorKey = "unity.openxr";
|
||||
private const string kEventInitialize = "openxr_initialize";
|
||||
|
||||
#if ENABLE_CLOUD_SERVICES_ANALYTICS && UNITY_ANALYTICS
|
||||
private static bool s_Initialized = false;
|
||||
#endif //ENABLE_CLOUD_SERVICES_ANALYTICS && UNITY_ANALYTICS
|
||||
|
||||
|
||||
[Serializable]
|
||||
private struct InitializeEvent
|
||||
#if UNITY_2023_2_OR_NEWER && ENABLE_CLOUD_SERVICES_ANALYTICS && UNITY_ANALYTICS
|
||||
: IAnalytic.IData
|
||||
#endif //UNITY_2023_2_OR_NEWER && ENABLE_CLOUD_SERVICES_ANALYTICS && UNITY_ANALYTICS
|
||||
{
|
||||
public bool success;
|
||||
public string runtime;
|
||||
public string runtime_version;
|
||||
public string plugin_version;
|
||||
public string api_version;
|
||||
public string[] available_extensions;
|
||||
public string[] enabled_extensions;
|
||||
public string[] enabled_features;
|
||||
public string[] failed_features;
|
||||
}
|
||||
|
||||
#if UNITY_2023_2_OR_NEWER && ENABLE_CLOUD_SERVICES_ANALYTICS && UNITY_ANALYTICS
|
||||
[AnalyticInfo(eventName: kEventInitialize, vendorKey: kVendorKey, maxEventsPerHour: kMaxEventsPerHour, maxNumberOfElements: kMaxNumberOfElements)]
|
||||
private class XrInitializeAnalytic : IAnalytic
|
||||
{
|
||||
private InitializeEvent? data = null;
|
||||
|
||||
public XrInitializeAnalytic(InitializeEvent data)
|
||||
{
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public bool TryGatherData(out IAnalytic.IData data, [NotNullWhen(false)] out Exception error)
|
||||
{
|
||||
error = null;
|
||||
data = this.data;
|
||||
return data != null;
|
||||
}
|
||||
}
|
||||
#endif //UNITY_2023_2_OR_NEWER && ENABLE_CLOUD_SERVICES_ANALYTICS && UNITY_ANALYTICS
|
||||
|
||||
private static bool Initialize()
|
||||
{
|
||||
#if ENABLE_TEST_SUPPORT || !ENABLE_CLOUD_SERVICES_ANALYTICS || !UNITY_ANALYTICS
|
||||
return false;
|
||||
#elif UNITY_EDITOR && UNITY_2023_2_OR_NEWER
|
||||
return EditorAnalytics.enabled;
|
||||
#else
|
||||
if (s_Initialized)
|
||||
return true;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!EditorAnalytics.enabled)
|
||||
return false;
|
||||
|
||||
if (AnalyticsResult.Ok != EditorAnalytics.RegisterEventWithLimit(kEventInitialize, kMaxEventsPerHour, kMaxNumberOfElements, kVendorKey))
|
||||
#else
|
||||
if (AnalyticsResult.Ok != Analytics.Analytics.RegisterEvent(kEventInitialize, kMaxEventsPerHour, kMaxNumberOfElements, kVendorKey))
|
||||
#endif //UNITY_EDITOR
|
||||
return false;
|
||||
|
||||
s_Initialized = true;
|
||||
|
||||
return true;
|
||||
#endif //ENABLE_TEST_SUPPORT || !ENABLE_CLOUD_SERVICES_ANALYTICS || !UNITY_ANALYTICS
|
||||
}
|
||||
|
||||
public static void SendInitializeEvent(bool success)
|
||||
{
|
||||
#if UNITY_ANALYTICS && ENABLE_CLOUD_SERVICES_ANALYTICS
|
||||
if (!s_Initialized && !Initialize())
|
||||
return;
|
||||
|
||||
var data = CreateInitializeEvent(success);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
SendEditorAnalytics(data);
|
||||
#else
|
||||
SendPlayerAnalytics(data);
|
||||
#endif //UNITY_EDITOR
|
||||
#endif //UNITY_ANALYTICS && ENABLE_CLOUD_SERVICES_ANALYTICS
|
||||
}
|
||||
|
||||
private static InitializeEvent CreateInitializeEvent(bool success)
|
||||
{
|
||||
return new InitializeEvent
|
||||
{
|
||||
success = success,
|
||||
runtime = OpenXRRuntime.name,
|
||||
runtime_version = OpenXRRuntime.version,
|
||||
plugin_version = OpenXRRuntime.pluginVersion,
|
||||
api_version = OpenXRRuntime.apiVersion,
|
||||
enabled_extensions = OpenXRRuntime.GetEnabledExtensions()
|
||||
.Select(ext => $"{ext}_{OpenXRRuntime.GetExtensionVersion(ext)}")
|
||||
.ToArray(),
|
||||
available_extensions = OpenXRRuntime.GetAvailableExtensions()
|
||||
.Select(ext => $"{ext}_{OpenXRRuntime.GetExtensionVersion(ext)}")
|
||||
.ToArray(),
|
||||
enabled_features = OpenXRSettings.Instance.features
|
||||
.Where(f => f != null && f.enabled)
|
||||
.Select(f => $"{f.GetType().FullName}_{f.version}").ToArray(),
|
||||
failed_features = OpenXRSettings.Instance.features
|
||||
.Where(f => f != null && f.failedInitialization)
|
||||
.Select(f => $"{f.GetType().FullName}_{f.version}").ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR && ENABLE_CLOUD_SERVICES_ANALYTICS && UNITY_ANALYTICS
|
||||
private static void SendEditorAnalytics(InitializeEvent data)
|
||||
{
|
||||
#if UNITY_2023_2_OR_NEWER
|
||||
EditorAnalytics.SendAnalytic(new XrInitializeAnalytic(data));
|
||||
#else
|
||||
EditorAnalytics.SendEventWithLimit(kEventInitialize, data);
|
||||
#endif //UNITY_2023_2_OR_NEWER
|
||||
}
|
||||
#elif UNITY_ANALYTICS && ENABLE_CLOUD_SERVICES_ANALYTICS
|
||||
private static void SendPlayerAnalytics(InitializeEvent data)
|
||||
{
|
||||
Analytics.Analytics.SendEvent(kEventInitialize, data);
|
||||
}
|
||||
#endif //UNITY_EDITOR && ENABLE_CLOUD_SERVICES_ANALYTICS && UNITY_ANALYTICS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2ee7eecb9814c689a865aa4312a7701
|
||||
timeCreated: 1605730739
|
||||
@@ -0,0 +1,377 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
#endif // UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
static class SerializedPropertyExt
|
||||
{
|
||||
public static void Update(this SerializedProperty property)
|
||||
{
|
||||
property.serializedObject.Update();
|
||||
}
|
||||
|
||||
public static void ApplyModifiedProperties(this SerializedProperty property)
|
||||
{
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
public static SerializedProperty GetArrayElementAtReverseIndex(
|
||||
this SerializedProperty property,
|
||||
int i
|
||||
)
|
||||
{
|
||||
return property.GetArrayElementAtIndex(property.arraySize - 1 - i);
|
||||
}
|
||||
|
||||
public static SerializedProperty GetLastArrayElement(this SerializedProperty property)
|
||||
{
|
||||
return property.GetArrayElementAtReverseIndex(0);
|
||||
}
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
public partial class OpenXRSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Runtime color format - keep in sync with NativePlugin\Source\unity_types.h
|
||||
/// </summary>
|
||||
public enum ColorSubmissionModeGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// Color render texture format, 8 bits per channel. This is the default swapchain
|
||||
/// format.
|
||||
/// Analogous to kUnityXRRenderTextureFormatRGBA32/BGRA32, auto-selecting the preferred
|
||||
/// color order for the platform.
|
||||
/// </summary>
|
||||
[InspectorName("8 bits per channel (LDR, default)")]
|
||||
kRenderTextureFormatGroup8888 = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Color render texture format. 10 bits for colors, 2 bits for alpha.
|
||||
/// Analogous to kUnityXRRenderTextureFormatRGBA1010102/BGRA1010102, auto-selecting the preferred
|
||||
/// color order for the platform.
|
||||
/// </summary>
|
||||
[InspectorName("10 bits floating-point per color channel, 2 bit alpha (HDR)")]
|
||||
kRenderTextureFormatGroup1010102_Float,
|
||||
|
||||
/// <summary>
|
||||
/// Color render texture format, 16 bit floating point per channel, auto-selecting the
|
||||
/// preferred color order for the platform.
|
||||
/// </summary>
|
||||
[InspectorName("16 bits floating-point per channel (HDR)")]
|
||||
kRenderTextureFormatGroup16161616_Float,
|
||||
|
||||
/// <summary>
|
||||
/// Color render texture format. 5 bits for Red channel, 6 bits for Green channel, 5
|
||||
/// bits for Blue channel, auto-selecting the preferred color order for the platform.
|
||||
/// </summary>
|
||||
[InspectorName("5,6,5 bit packed (LDR, mobile)")]
|
||||
kRenderTextureFormatGroup565,
|
||||
|
||||
/// <summary>
|
||||
/// Color render texture format. R and G channels are 11 bit floating point, B channel is 10
|
||||
/// bit floating point.
|
||||
/// </summary>
|
||||
[InspectorName("11,11,10 bit packed floating-point (HDR)")]
|
||||
kRenderTextureFormatGroup111110_Float,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default color mode for color render targets.
|
||||
/// </summary>
|
||||
public static readonly ColorSubmissionModeGroup kDefaultColorMode =
|
||||
ColorSubmissionModeGroup.kRenderTextureFormatGroup8888;
|
||||
|
||||
/// <summary>
|
||||
/// Container class for the list of color render target color modes, used to enable a CustomPropertyDrawer.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ColorSubmissionModeList
|
||||
{
|
||||
/// <summary>
|
||||
/// The priority list of color render target color modes. The first _supported_ format is selected.
|
||||
/// </summary>
|
||||
public ColorSubmissionModeGroup[] m_List =
|
||||
{
|
||||
ColorSubmissionModeGroup.kRenderTextureFormatGroup8888
|
||||
};
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomPropertyDrawer(typeof(ColorSubmissionModeList))]
|
||||
internal class ColorSubmissionModeListEditor : UnityEditor.PropertyDrawer
|
||||
{
|
||||
private struct FormatGroupInfo
|
||||
{
|
||||
public ColorSubmissionModeGroup Value;
|
||||
public string Name;
|
||||
public string DisplayName;
|
||||
}
|
||||
|
||||
private List<FormatGroupInfo> m_GroupInfo;
|
||||
private ReorderableList m_ListGUI;
|
||||
private HashSet<int> m_FormatIndicesInUse = new HashSet<int>();
|
||||
private HashSet<ColorSubmissionModeGroup> m_FormatValuesInUse =
|
||||
new HashSet<ColorSubmissionModeGroup>();
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
property = property?.FindPropertyRelative("m_List");
|
||||
if (property == null || !property.isArray)
|
||||
return;
|
||||
|
||||
property.Update();
|
||||
|
||||
bool hasDefault = false;
|
||||
for (int i = 0; i < property.arraySize; i++)
|
||||
{
|
||||
var element = property.GetArrayElementAtIndex(i);
|
||||
if (element.intValue == (int)kDefaultColorMode)
|
||||
{
|
||||
hasDefault = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasDefault)
|
||||
{
|
||||
property.arraySize++;
|
||||
property.GetLastArrayElement().intValue = (int)kDefaultColorMode;
|
||||
}
|
||||
|
||||
var listSize = property.arraySize;
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
GUILayout.Label("Color Submission Modes");
|
||||
|
||||
if (m_ListGUI == null)
|
||||
{
|
||||
m_ListGUI = new ReorderableList(null, property);
|
||||
m_ListGUI.onAddDropdownCallback = (position, listGUI) =>
|
||||
AddDropdownCallback(property, position, listGUI);
|
||||
m_ListGUI.onCanRemoveCallback = (listGUI) =>
|
||||
{
|
||||
property.Update();
|
||||
return property.arraySize > 1
|
||||
&& property.GetArrayElementAtIndex(listGUI.index).intValue
|
||||
!= (int)kDefaultColorMode;
|
||||
};
|
||||
m_ListGUI.onRemoveCallback = (listGUI) =>
|
||||
{
|
||||
using (new PropertyScope(property))
|
||||
property.DeleteArrayElementAtIndex(listGUI.index);
|
||||
};
|
||||
m_ListGUI.onReorderCallbackWithDetails = (listGUI, oldIndex, newIndex) =>
|
||||
OnReorderCallbackWithDetails(property, oldIndex, newIndex);
|
||||
m_ListGUI.drawElementCallback = (position, index, isActive, isFocused) =>
|
||||
DrawElementCallback(property, position, index);
|
||||
m_ListGUI.drawHeaderCallback = (position) =>
|
||||
GUI.Label(position, "Color Formats");
|
||||
m_ListGUI.elementHeight = 16;
|
||||
m_ListGUI.multiSelect = false;
|
||||
}
|
||||
m_ListGUI.DoLayoutList();
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
property.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void OnReorderCallbackWithDetails(
|
||||
SerializedProperty property,
|
||||
int oldIndex,
|
||||
int newIndex
|
||||
)
|
||||
{
|
||||
if (property == null || !property.isArray)
|
||||
return;
|
||||
|
||||
using (new PropertyScope(property))
|
||||
{
|
||||
SerializedProperty oldLocation = property.GetArrayElementAtIndex(oldIndex);
|
||||
SerializedProperty newLocation = property.GetArrayElementAtIndex(newIndex);
|
||||
int tempValue = newLocation.enumValueIndex;
|
||||
newLocation.enumValueIndex = oldLocation.enumValueIndex;
|
||||
oldLocation.enumValueIndex = tempValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawElementCallback(SerializedProperty property, Rect position, int index)
|
||||
{
|
||||
var element = property.GetArrayElementAtIndex(index);
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
// Don't allow removing RGBA32 - default.
|
||||
if (element.intValue == (int)kDefaultColorMode)
|
||||
{
|
||||
GUI.Label(position, element.enumDisplayNames[element.enumValueIndex]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
EditorGUI.DropdownButton(
|
||||
position: position,
|
||||
content: new GUIContent(element.enumDisplayNames[element.enumValueIndex]),
|
||||
focusType: FocusType.Keyboard
|
||||
)
|
||||
)
|
||||
{
|
||||
var menu = new GenericMenu();
|
||||
|
||||
var inUse = FormatValuesInUse(property);
|
||||
AvailableFormatGroups(property)
|
||||
.ForEach(gi =>
|
||||
{
|
||||
if (inUse.Contains(gi.Value))
|
||||
menu.AddDisabledItem(new GUIContent(gi.DisplayName));
|
||||
else
|
||||
menu.AddItem(
|
||||
new GUIContent(gi.DisplayName),
|
||||
on: false,
|
||||
func: () =>
|
||||
{
|
||||
using (new PropertyScope(property))
|
||||
{
|
||||
element.intValue = (int)gi.Value;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
menu.DropDown(position);
|
||||
}
|
||||
}
|
||||
|
||||
private HashSet<int> FormatIndicesInUse(SerializedProperty property)
|
||||
{
|
||||
m_FormatIndicesInUse.Clear();
|
||||
if (property == null || !property.isArray)
|
||||
return m_FormatIndicesInUse;
|
||||
|
||||
for (int i = 0; i < property.arraySize; i++)
|
||||
{
|
||||
m_FormatIndicesInUse.Add(property.GetArrayElementAtIndex(i).enumValueIndex);
|
||||
}
|
||||
return m_FormatIndicesInUse;
|
||||
}
|
||||
|
||||
private HashSet<ColorSubmissionModeGroup> FormatValuesInUse(SerializedProperty property)
|
||||
{
|
||||
m_FormatValuesInUse.Clear();
|
||||
if (property == null || !property.isArray)
|
||||
return m_FormatValuesInUse;
|
||||
|
||||
for (int i = 0; i < property.arraySize; i++)
|
||||
{
|
||||
m_FormatValuesInUse.Add(
|
||||
(ColorSubmissionModeGroup)property.GetArrayElementAtIndex(i).intValue
|
||||
);
|
||||
}
|
||||
return m_FormatValuesInUse;
|
||||
}
|
||||
|
||||
private List<FormatGroupInfo> AvailableFormatGroups(SerializedProperty property)
|
||||
{
|
||||
if (m_GroupInfo == null)
|
||||
{
|
||||
m_GroupInfo = new List<FormatGroupInfo>();
|
||||
|
||||
var values = Enum.GetValues(typeof(ColorSubmissionModeGroup))
|
||||
.Cast<ColorSubmissionModeGroup>();
|
||||
if (Internal_GetIsUsingLegacyXRDisplay())
|
||||
{
|
||||
values = new List<ColorSubmissionModeGroup>
|
||||
{
|
||||
ColorSubmissionModeGroup.kRenderTextureFormatGroup8888,
|
||||
ColorSubmissionModeGroup.kRenderTextureFormatGroup565
|
||||
};
|
||||
}
|
||||
|
||||
var element0 = property.GetArrayElementAtIndex(0);
|
||||
var displayNames = element0.enumDisplayNames;
|
||||
var names = element0.enumNames;
|
||||
|
||||
foreach (var v in values)
|
||||
{
|
||||
var index = names
|
||||
.Select((n, i) => new { name = n, index = i })
|
||||
.First(s => s.name == Enum.GetName(typeof(ColorSubmissionModeGroup), v))
|
||||
.index;
|
||||
m_GroupInfo.Add(
|
||||
new FormatGroupInfo
|
||||
{
|
||||
Value = v,
|
||||
Name = names[index],
|
||||
DisplayName = displayNames[index]
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return m_GroupInfo;
|
||||
}
|
||||
|
||||
private void AddDropdownCallback(
|
||||
SerializedProperty property,
|
||||
Rect position,
|
||||
ReorderableList listGUI
|
||||
)
|
||||
{
|
||||
using (new PropertyScope(property))
|
||||
EditorUtility.DisplayCustomMenu(
|
||||
position,
|
||||
AvailableFormatGroups(property)
|
||||
.Select(gi => new GUIContent(gi.DisplayName))
|
||||
.ToArray(),
|
||||
(i => !FormatIndicesInUse(property).Contains(i)),
|
||||
-1,
|
||||
MenuSelectCallback,
|
||||
property
|
||||
);
|
||||
}
|
||||
|
||||
private void MenuSelectCallback(object userData, string[] options, int selected)
|
||||
{
|
||||
SerializedProperty property = (SerializedProperty)userData;
|
||||
if (property == null || !property.isArray)
|
||||
return;
|
||||
|
||||
using (new PropertyScope(property))
|
||||
{
|
||||
property.arraySize++;
|
||||
property.GetLastArrayElement().intValue = (int)
|
||||
AvailableFormatGroups(property)[selected].Value;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PropertyScope : IDisposable
|
||||
{
|
||||
SerializedProperty m_Property;
|
||||
|
||||
public PropertyScope(SerializedProperty property)
|
||||
{
|
||||
m_Property = property;
|
||||
m_Property.Update();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_Property.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49b278274da7a9d42bf6f89bd5959b57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
25
Packages/com.unity.xr.openxr/Runtime/OpenXRConstants.cs
Normal file
25
Packages/com.unity.xr.openxr/Runtime/OpenXRConstants.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
/// <summary>
|
||||
/// Static constants
|
||||
/// </summary>
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Key used to store and retrieve custom configuration settings from EditorBuildSettings.
|
||||
/// </summary>
|
||||
public const string k_SettingsKey = "com.unity.xr.openxr.settings4";
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Root URL for the OpenXR documentation
|
||||
/// </summary>
|
||||
public const string k_DocumentationManualURL = "https://docs.unity3d.com/Packages/com.unity.xr.openxr@1.0/manual/";
|
||||
|
||||
/// <summary>
|
||||
/// Main documentation URL for OpenXR
|
||||
/// </summary>
|
||||
public const string k_DocumentationURL = k_DocumentationManualURL + "index.html";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.xr.openxr/Runtime/OpenXRConstants.cs.meta
Normal file
11
Packages/com.unity.xr.openxr/Runtime/OpenXRConstants.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60ca6545650dbad4a9b77c456f8463ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
internal class DiagnosticReport
|
||||
{
|
||||
private const string LibraryName = "UnityOpenXR";
|
||||
public static readonly ulong k_NullSection = 0;
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "DiagnosticReport_StartReport")]
|
||||
public static extern void StartReport();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "DiagnosticReport_GetSection", CharSet = CharSet.Ansi)]
|
||||
public static extern ulong GetSection(string sectionName);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "DiagnosticReport_AddSectionEntry", CharSet = CharSet.Ansi)]
|
||||
public static extern void AddSectionEntry(ulong sectionHandle, string sectionEntry, string sectionBody);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "DiagnosticReport_AddSectionBreak", CharSet = CharSet.Ansi)]
|
||||
public static extern void AddSectionBreak(ulong sectionHandle);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "DiagnosticReport_AddEventEntry", CharSet = CharSet.Ansi)]
|
||||
public static extern void AddEventEntry(string eventName, string eventData);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "DiagnosticReport_DumpReport")]
|
||||
private static extern void Internal_DumpReport();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "DiagnosticReport_DumpReportWithReason")]
|
||||
private static extern void Internal_DumpReport(string reason);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "DiagnosticReport_GenerateReport")]
|
||||
static extern IntPtr Internal_GenerateReport();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "DiagnosticReport_ReleaseReport")]
|
||||
static extern void Internal_ReleaseReport(IntPtr report);
|
||||
|
||||
internal static string GenerateReport()
|
||||
{
|
||||
string ret = "";
|
||||
|
||||
IntPtr buffer = Internal_GenerateReport();
|
||||
if (buffer != IntPtr.Zero)
|
||||
{
|
||||
ret = Marshal.PtrToStringAnsi(buffer);
|
||||
Internal_ReleaseReport(buffer);
|
||||
buffer = IntPtr.Zero;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void DumpReport(string reason)
|
||||
{
|
||||
Internal_DumpReport(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0a68818db30b844ebe06958ebd83f38
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
762
Packages/com.unity.xr.openxr/Runtime/OpenXRLoader.cs
Normal file
762
Packages/com.unity.xr.openxr/Runtime/OpenXRLoader.cs
Normal file
@@ -0,0 +1,762 @@
|
||||
#if UNITY_EDITOR && ENABLE_TEST_SUPPORT
|
||||
#define TEST_SUPPORT
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.Management;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.Management;
|
||||
using UnityEditor.XR.OpenXR;
|
||||
#endif
|
||||
|
||||
[assembly: Preserve]
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.XR.OpenXR.TestHelpers")]
|
||||
[assembly: InternalsVisibleTo("Unity.XR.OpenXR.Tests")]
|
||||
[assembly: InternalsVisibleTo("Unity.XR.OpenXR.Tests.Editor")]
|
||||
[assembly: InternalsVisibleTo("Unity.XR.OpenXR.Editor")]
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
/// <summary>
|
||||
/// Loader for the OpenXR Plug-in. Used by [XR Plug-in Management](https://docs.unity3d.com/Packages/com.unity.xr.management@latest) to manage OpenXR lifecycle.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[XRSupportedBuildTarget(BuildTargetGroup.Standalone, new BuildTarget[] {BuildTarget.StandaloneWindows64})]
|
||||
[XRSupportedBuildTarget(BuildTargetGroup.Android)]
|
||||
[XRSupportedBuildTarget(BuildTargetGroup.WSA)]
|
||||
#endif
|
||||
public class OpenXRLoader : OpenXRLoaderBase
|
||||
#if UNITY_EDITOR
|
||||
, IXRLoaderPreInit
|
||||
#endif
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
public string GetPreInitLibraryName(BuildTarget buildTarget, BuildTargetGroup buildTargetGroup)
|
||||
{
|
||||
return "UnityOpenXR";
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base abstract class to hold common loader code.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
// Hide this by default from the UI by setting to Unknown.
|
||||
[XRSupportedBuildTarget(BuildTargetGroup.Unknown)]
|
||||
#endif
|
||||
public partial class OpenXRLoaderBase : XRLoaderHelper
|
||||
{
|
||||
private class FeatureLoggingInfo
|
||||
{
|
||||
public FeatureLoggingInfo(string nameUi, string version, string company, string extensionStrings)
|
||||
{
|
||||
m_nameUi = nameUi;
|
||||
m_version = version;
|
||||
m_company = company;
|
||||
m_openxrExtensionStrings = extensionStrings;
|
||||
}
|
||||
|
||||
public string m_nameUi;
|
||||
public string m_version;
|
||||
public string m_company;
|
||||
public string m_openxrExtensionStrings;
|
||||
}
|
||||
|
||||
private List<FeatureLoggingInfo> featureLoggingInfo;
|
||||
|
||||
const double k_IdlePollingWaitTimeInSeconds = 0.1;
|
||||
private static List<XRDisplaySubsystemDescriptor> s_DisplaySubsystemDescriptors =
|
||||
new List<XRDisplaySubsystemDescriptor>();
|
||||
private static List<XRInputSubsystemDescriptor> s_InputSubsystemDescriptors =
|
||||
new List<XRInputSubsystemDescriptor>();
|
||||
|
||||
/// <summary>
|
||||
/// Represents the running OpenXRLoader instance. This value should be non null after calling
|
||||
/// Initialize until a subsequent call to DeInitialize is made.
|
||||
/// </summary>
|
||||
internal static OpenXRLoaderBase Instance { get; private set; }
|
||||
|
||||
internal enum LoaderState
|
||||
{
|
||||
Uninitialized,
|
||||
InitializeAttempted,
|
||||
Initialized,
|
||||
StartAttempted,
|
||||
Started,
|
||||
StopAttempted,
|
||||
Stopped,
|
||||
DeinitializeAttempted
|
||||
}
|
||||
|
||||
internal LoaderState currentLoaderState { get; private set; } = LoaderState.Uninitialized;
|
||||
|
||||
List<LoaderState> validLoaderInitStates = new List<LoaderState> { LoaderState.Uninitialized, LoaderState.InitializeAttempted };
|
||||
List<LoaderState> validLoaderStartStates = new List<LoaderState> { LoaderState.Initialized, LoaderState.StartAttempted, LoaderState.Stopped };
|
||||
List<LoaderState> validLoaderStopStates = new List<LoaderState> { LoaderState.StartAttempted, LoaderState.Started, LoaderState.StopAttempted };
|
||||
List<LoaderState> validLoaderDeinitStates = new List<LoaderState> { LoaderState.InitializeAttempted, LoaderState.Initialized, LoaderState.Stopped, LoaderState.DeinitializeAttempted };
|
||||
|
||||
List<LoaderState> runningStates = new List<LoaderState>()
|
||||
{
|
||||
LoaderState.Initialized,
|
||||
LoaderState.StartAttempted,
|
||||
LoaderState.Started
|
||||
};
|
||||
|
||||
#if TEST_SUPPORT
|
||||
[NonSerialized]
|
||||
internal LoaderState targetLoaderState;
|
||||
|
||||
bool ShouldExitEarly()
|
||||
{
|
||||
return (currentLoaderState == targetLoaderState);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
OpenXRFeature.NativeEvent currentOpenXRState;
|
||||
private bool actionSetsAttached;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the current display subsystem if the loader is initialized, or null if the loader is not initialized.
|
||||
/// </summary>
|
||||
internal XRDisplaySubsystem displaySubsystem => GetLoadedSubsystem<XRDisplaySubsystem>();
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the current input subsystem if the loader is initialized, or null if the loader is not initialized.
|
||||
/// </summary>
|
||||
internal XRInputSubsystem inputSubsystem => Instance?.GetLoadedSubsystem<XRInputSubsystem>();
|
||||
|
||||
/// <summary>
|
||||
/// True if the loader has been initialized, false otherwise.
|
||||
/// </summary>
|
||||
private bool isInitialized =>
|
||||
currentLoaderState != LoaderState.Uninitialized &&
|
||||
currentLoaderState != LoaderState.DeinitializeAttempted;
|
||||
|
||||
/// <summary>
|
||||
/// True if the loader has been started, false otherwise.
|
||||
/// </summary>
|
||||
private bool isStarted => runningStates.Contains(currentLoaderState);
|
||||
|
||||
private UnhandledExceptionEventHandler unhandledExceptionHandler = null;
|
||||
|
||||
internal bool DisableValidationChecksOnEnteringPlaymode = false;
|
||||
|
||||
static void ExceptionHandler(object sender, UnhandledExceptionEventArgs args)
|
||||
{
|
||||
var section = DiagnosticReport.GetSection("Unhandled Exception Report");
|
||||
DiagnosticReport.AddSectionEntry(section, "Is Terminating", $"{args.IsTerminating}");
|
||||
|
||||
var e = (Exception)args.ExceptionObject;
|
||||
|
||||
DiagnosticReport.AddSectionEntry(section, "Message", $"{e.Message}");
|
||||
DiagnosticReport.AddSectionEntry(section, "Source", $"{e.Source}");
|
||||
DiagnosticReport.AddSectionEntry(section, "Stack Trace", $"\n{e.StackTrace}");
|
||||
|
||||
DiagnosticReport.DumpReport("Uncaught Exception");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See [XRLoader.Initialize](xref:UnityEngine.XR.Management.XRLoader.Initialize)
|
||||
/// </summary>
|
||||
/// <returns>True if initialized, false otherwise.</returns>
|
||||
public override bool Initialize()
|
||||
{
|
||||
if (currentLoaderState == LoaderState.Initialized)
|
||||
return true;
|
||||
|
||||
if (!validLoaderInitStates.Contains(currentLoaderState))
|
||||
return false;
|
||||
|
||||
if (Instance != null)
|
||||
{
|
||||
Debug.LogError("Only one OpenXRLoader can be initialized at any given time");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!DisableValidationChecksOnEnteringPlaymode)
|
||||
{
|
||||
if (OpenXRProjectValidation.LogPlaymodeValidationIssues())
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
DiagnosticReport.StartReport();
|
||||
|
||||
// Wrap the initialization in a try catch block to ensure if any exceptions are thrown that
|
||||
// we cleanup, otherwise the user will not be able to run again until they restart the editor.
|
||||
try
|
||||
{
|
||||
if (InitializeInternal())
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
|
||||
Deinitialize();
|
||||
Instance = null;
|
||||
OpenXRAnalytics.SendInitializeEvent(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool InitializeInternal()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
currentLoaderState = LoaderState.InitializeAttempted;
|
||||
|
||||
#if TEST_SUPPORT
|
||||
if (ShouldExitEarly()) return false;
|
||||
#endif
|
||||
OpenXRLoaderBase.Internal_SetSuccessfullyInitialized(false);
|
||||
|
||||
OpenXRInput.RegisterLayouts();
|
||||
|
||||
OpenXRFeature.Initialize();
|
||||
|
||||
if (!LoadOpenXRSymbols())
|
||||
{
|
||||
Debug.LogError("Failed to load openxr runtime loader.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort the features array by priority in descending order (highest priority first)
|
||||
OpenXRSettings.Instance.features = OpenXRSettings.Instance.features
|
||||
.Where(f => f != null)
|
||||
.OrderByDescending(f => f.priority)
|
||||
.ThenBy(f => f.nameUi)
|
||||
.ToArray();
|
||||
|
||||
OpenXRFeature.HookGetInstanceProcAddr();
|
||||
|
||||
if (!Internal_InitializeSession())
|
||||
return false;
|
||||
|
||||
RequestOpenXRFeatures();
|
||||
RegisterOpenXRCallbacks();
|
||||
|
||||
if (null != OpenXRSettings.Instance)
|
||||
OpenXRSettings.Instance.ApplySettings();
|
||||
|
||||
if (!CreateSubsystems())
|
||||
return false;
|
||||
|
||||
if (OpenXRFeature.requiredFeatureFailed)
|
||||
return false;
|
||||
|
||||
SetApplicationInfo();
|
||||
OpenXRAnalytics.SendInitializeEvent(true);
|
||||
|
||||
OpenXRFeature.ReceiveLoaderEvent(this, OpenXRFeature.LoaderEvent.SubsystemCreate);
|
||||
|
||||
DebugLogEnabledSpecExtensions();
|
||||
|
||||
Application.onBeforeRender += ProcessOpenXRMessageLoop;
|
||||
currentLoaderState = LoaderState.Initialized;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CreateSubsystems()
|
||||
{
|
||||
// NOTE: This function is only necessary to handle subsystems being lost after domain reload. If that issue is fixed
|
||||
// at the management level the code below can be folded back into Initialize
|
||||
// NOTE: Below we check to see if a subsystem is already created before creating it. This is done because we currently
|
||||
// re-create the subsystems after a domain reload to fix a deficiency in XR Managements handling of domain reload. To
|
||||
// ensure we properly handle a fix to that deficiency we first check to make sure the subsystems are not already created.
|
||||
|
||||
if (displaySubsystem == null)
|
||||
{
|
||||
CreateSubsystem<XRDisplaySubsystemDescriptor, XRDisplaySubsystem>(s_DisplaySubsystemDescriptors, "OpenXR Display");
|
||||
if (displaySubsystem == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inputSubsystem == null)
|
||||
{
|
||||
CreateSubsystem<XRInputSubsystemDescriptor, XRInputSubsystem>(s_InputSubsystemDescriptors, "OpenXR Input");
|
||||
if (inputSubsystem == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private double lastPollCheckTime = 0;
|
||||
|
||||
internal void ProcessOpenXRMessageLoop()
|
||||
{
|
||||
if (currentOpenXRState == OpenXRFeature.NativeEvent.XrIdle ||
|
||||
currentOpenXRState == OpenXRFeature.NativeEvent.XrStopping ||
|
||||
currentOpenXRState == OpenXRFeature.NativeEvent.XrExiting ||
|
||||
currentOpenXRState == OpenXRFeature.NativeEvent.XrLossPending ||
|
||||
currentOpenXRState == OpenXRFeature.NativeEvent.XrInstanceLossPending)
|
||||
{
|
||||
var time = Time.realtimeSinceStartup;
|
||||
|
||||
if ((time - lastPollCheckTime) < k_IdlePollingWaitTimeInSeconds)
|
||||
return;
|
||||
|
||||
lastPollCheckTime = time;
|
||||
}
|
||||
|
||||
Internal_PumpMessageLoop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See [XRLoader.Start](xref:UnityEngine.XR.Management.XRLoader.Start)
|
||||
/// </summary>
|
||||
/// <returns>True if started, false otherwise.</returns>
|
||||
public override bool Start()
|
||||
{
|
||||
if (currentLoaderState == LoaderState.Started)
|
||||
return true;
|
||||
|
||||
if (!validLoaderStartStates.Contains(currentLoaderState))
|
||||
return false;
|
||||
|
||||
currentLoaderState = LoaderState.StartAttempted;
|
||||
|
||||
#if TEST_SUPPORT
|
||||
if (ShouldExitEarly()) return false;
|
||||
#endif
|
||||
|
||||
if (!StartInternal())
|
||||
{
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
currentLoaderState = LoaderState.Started;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool StartInternal()
|
||||
{
|
||||
// In order to get XrReady, we have to at least attempt to create
|
||||
// the session if it isn't already there.
|
||||
if (!Internal_CreateSessionIfNeeded())
|
||||
return false;
|
||||
|
||||
if (currentOpenXRState != OpenXRFeature.NativeEvent.XrReady ||
|
||||
(currentLoaderState != LoaderState.StartAttempted && currentLoaderState != LoaderState.Started))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note: Display has to be started before Input so that Input can have access to the Session object
|
||||
StartSubsystem<XRDisplaySubsystem>();
|
||||
if (!displaySubsystem?.running ?? false)
|
||||
return false;
|
||||
|
||||
// calls xrBeginSession
|
||||
Internal_BeginSession();
|
||||
|
||||
if (!actionSetsAttached)
|
||||
{
|
||||
OpenXRInput.AttachActionSets();
|
||||
actionSetsAttached = true;
|
||||
}
|
||||
|
||||
if (!displaySubsystem?.running ?? false)
|
||||
StartSubsystem<XRDisplaySubsystem>();
|
||||
|
||||
if (!inputSubsystem?.running ?? false)
|
||||
StartSubsystem<XRInputSubsystem>();
|
||||
|
||||
var inputRunning = inputSubsystem?.running ?? false;
|
||||
var displayRunning = displaySubsystem?.running ?? false;
|
||||
|
||||
if (inputRunning && displayRunning)
|
||||
{
|
||||
OpenXRFeature.ReceiveLoaderEvent(this, OpenXRFeature.LoaderEvent.SubsystemStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See [XRLoader.Stop](xref:UnityEngine.XR.Management.XRLoader.Stop)
|
||||
/// </summary>
|
||||
/// <returns>True if stopped, false otherwise.</returns>
|
||||
public override bool Stop()
|
||||
{
|
||||
if (currentLoaderState == LoaderState.Stopped)
|
||||
return true;
|
||||
|
||||
if (!validLoaderStopStates.Contains(currentLoaderState))
|
||||
return false;
|
||||
|
||||
currentLoaderState = LoaderState.StopAttempted;
|
||||
|
||||
#if TEST_SUPPORT
|
||||
if (ShouldExitEarly()) return false;
|
||||
#endif
|
||||
|
||||
var inputRunning = inputSubsystem?.running ?? false;
|
||||
var displayRunning = displaySubsystem?.running ?? false;
|
||||
|
||||
if (inputRunning || displayRunning)
|
||||
OpenXRFeature.ReceiveLoaderEvent(this, OpenXRFeature.LoaderEvent.SubsystemStop);
|
||||
|
||||
if (inputRunning)
|
||||
StopSubsystem<XRInputSubsystem>();
|
||||
|
||||
if (displayRunning)
|
||||
StopSubsystem<XRDisplaySubsystem>();
|
||||
StopInternal();
|
||||
|
||||
currentLoaderState = LoaderState.Stopped;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void StopInternal()
|
||||
{
|
||||
Internal_EndSession();
|
||||
|
||||
ProcessOpenXRMessageLoop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See [XRLoader.DeInitialize](xref:UnityEngine.XR.Management.XRLoader.Stop)
|
||||
/// </summary>
|
||||
/// <returns>True if deinitialized, false otherwise.</returns>
|
||||
public override bool Deinitialize()
|
||||
{
|
||||
if (currentLoaderState == LoaderState.Uninitialized)
|
||||
return true;
|
||||
|
||||
if (!validLoaderDeinitStates.Contains(currentLoaderState))
|
||||
return false;
|
||||
|
||||
currentLoaderState = LoaderState.DeinitializeAttempted;
|
||||
|
||||
try
|
||||
{
|
||||
#if TEST_SUPPORT
|
||||
if (ShouldExitEarly()) return false;
|
||||
|
||||
// The test hook above will leave the loader in a half initialized state. To work
|
||||
// around this we reset the instance pointer if it is missing.
|
||||
if (Instance == null)
|
||||
Instance = this;
|
||||
#endif
|
||||
Internal_RequestExitSession();
|
||||
|
||||
Application.onBeforeRender -= ProcessOpenXRMessageLoop;
|
||||
|
||||
ProcessOpenXRMessageLoop(); // Drain any remaining events.
|
||||
|
||||
OpenXRFeature.ReceiveLoaderEvent(this, OpenXRFeature.LoaderEvent.SubsystemDestroy);
|
||||
|
||||
DestroySubsystem<XRInputSubsystem>();
|
||||
DestroySubsystem<XRDisplaySubsystem>();
|
||||
|
||||
DiagnosticReport.DumpReport("System Shutdown");
|
||||
|
||||
Internal_DestroySession();
|
||||
|
||||
ProcessOpenXRMessageLoop();
|
||||
|
||||
Internal_UnloadOpenXRLibrary();
|
||||
|
||||
currentLoaderState = LoaderState.Uninitialized;
|
||||
actionSetsAttached = false;
|
||||
|
||||
if (unhandledExceptionHandler != null)
|
||||
{
|
||||
AppDomain currentDomain = AppDomain.CurrentDomain;
|
||||
currentDomain.UnhandledException -= unhandledExceptionHandler;
|
||||
unhandledExceptionHandler = null;
|
||||
}
|
||||
|
||||
return base.Deinitialize();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Ensure we always clear the instance reference even if some part of Deinitialize threw an exception
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal new void CreateSubsystem<TDescriptor, TSubsystem>(List<TDescriptor> descriptors, string id)
|
||||
where TDescriptor : ISubsystemDescriptor
|
||||
where TSubsystem : ISubsystem
|
||||
{
|
||||
base.CreateSubsystem<TDescriptor, TSubsystem>(descriptors, id);
|
||||
}
|
||||
|
||||
internal new void StartSubsystem<T>() where T : class, ISubsystem => base.StartSubsystem<T>();
|
||||
|
||||
internal new void StopSubsystem<T>() where T : class, ISubsystem => base.StopSubsystem<T>();
|
||||
|
||||
internal new void DestroySubsystem<T>() where T : class, ISubsystem => base.DestroySubsystem<T>();
|
||||
|
||||
private void SetApplicationInfo()
|
||||
{
|
||||
var md5 = MD5.Create();
|
||||
byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(Application.version));
|
||||
if (BitConverter.IsLittleEndian)
|
||||
Array.Reverse(data);
|
||||
uint applicationVersionHash = BitConverter.ToUInt32(data, 0);
|
||||
|
||||
Internal_SetApplicationInfo(Application.productName, Application.version, applicationVersionHash, Application.unityVersion);
|
||||
}
|
||||
|
||||
internal static byte[] StringToWCHAR_T(string s)
|
||||
{
|
||||
var encoding = Environment.OSVersion.Platform == PlatformID.Unix ? Encoding.UTF32 : Encoding.Unicode;
|
||||
return encoding.GetBytes(s + '\0');
|
||||
}
|
||||
|
||||
private bool LoadOpenXRSymbols()
|
||||
{
|
||||
string loaderPath = "openxr_loader";
|
||||
|
||||
#if UNITY_EDITOR_WIN
|
||||
loaderPath = "..\\..\\..\\RuntimeLoaders\\windows\\x64\\openxr_loader";
|
||||
#elif UNITY_EDITOR_OSX
|
||||
// no loader for osx, use the mock by default
|
||||
loaderPath = $"../../MockRuntime/osx/openxr_loader";
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Pass down active loader path to plugin
|
||||
EditorBuildSettings.TryGetConfigObject<UnityEngine.Object>(Constants.k_SettingsKey, out var obj);
|
||||
if (obj != null && (obj is IPackageSettings packageSettings))
|
||||
{
|
||||
var extensionLoaderPath = packageSettings.GetActiveLoaderLibraryPath();
|
||||
if (!String.IsNullOrEmpty(extensionLoaderPath))
|
||||
loaderPath = extensionLoaderPath;
|
||||
}
|
||||
#endif
|
||||
if (!Internal_LoadOpenXRLibrary(StringToWCHAR_T(loaderPath)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RequestOpenXRFeatures()
|
||||
{
|
||||
var instance = OpenXRSettings.Instance;
|
||||
if (instance == null || instance.features == null)
|
||||
return;
|
||||
|
||||
featureLoggingInfo = new List<FeatureLoggingInfo>(instance.featureCount);
|
||||
|
||||
foreach (var feature in instance.features)
|
||||
{
|
||||
if (feature == null || !feature.enabled)
|
||||
continue;
|
||||
|
||||
// Store feature logging info to be logged later.
|
||||
// We need to log this after we've determined the version of the OpenXR Runtime
|
||||
featureLoggingInfo.Add(new FeatureLoggingInfo(feature.nameUi, feature.version, feature.company, feature.openxrExtensionStrings));
|
||||
|
||||
if (!string.IsNullOrEmpty(feature.openxrExtensionStrings))
|
||||
{
|
||||
// Check to see if any of the required extensions are not supported by the runtime
|
||||
foreach (var extensionString in feature.openxrExtensionStrings.Split(' '))
|
||||
{
|
||||
// Request each extension.
|
||||
if (string.IsNullOrWhiteSpace(extensionString)) continue;
|
||||
Internal_RequestEnableExtensionString(extensionString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LogRequestedOpenXRFeatures()
|
||||
{
|
||||
var instance = OpenXRSettings.Instance;
|
||||
if (instance == null || instance.features == null)
|
||||
return;
|
||||
|
||||
StringBuilder requestedLog = new StringBuilder("");
|
||||
StringBuilder failedLog = new StringBuilder("");
|
||||
uint count = 0;
|
||||
uint failedCount = 0;
|
||||
foreach (var feature in featureLoggingInfo)
|
||||
{
|
||||
requestedLog.Append($" {feature.m_nameUi}: Version={feature.m_version}, Company=\"{feature.m_company}\"");
|
||||
|
||||
if (!string.IsNullOrEmpty(feature.m_openxrExtensionStrings))
|
||||
{
|
||||
requestedLog.Append($", Extensions=\"{feature.m_openxrExtensionStrings}\"");
|
||||
|
||||
// Check to see if any of the required extensions are not supported by the runtime
|
||||
foreach (var extensionString in feature.m_openxrExtensionStrings.Split(' '))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(extensionString)) continue;
|
||||
if (!Internal_IsExtensionEnabled(extensionString))
|
||||
{
|
||||
++failedCount;
|
||||
failedLog.Append($" {extensionString}: Feature=\"{feature.m_nameUi}\": Version={feature.m_version}, Company=\"{feature.m_company}\"\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestedLog.Append("\n");
|
||||
}
|
||||
|
||||
var section = DiagnosticReport.GetSection("OpenXR Runtime Info");
|
||||
DiagnosticReport.AddSectionBreak(section);
|
||||
DiagnosticReport.AddSectionEntry(section, "Features requested to be enabled", $"({count})\n{requestedLog.ToString()}");
|
||||
DiagnosticReport.AddSectionBreak(section);
|
||||
DiagnosticReport.AddSectionEntry(section, "Requested feature extensions not supported by runtime", $"({failedCount})\n{failedLog.ToString()}");
|
||||
}
|
||||
|
||||
private static void DebugLogEnabledSpecExtensions()
|
||||
{
|
||||
var section = DiagnosticReport.GetSection("OpenXR Runtime Info");
|
||||
DiagnosticReport.AddSectionBreak(section);
|
||||
|
||||
var extensions = OpenXRRuntime.GetEnabledExtensions();
|
||||
var log = new StringBuilder($"({extensions.Length})\n");
|
||||
foreach (var extension in extensions)
|
||||
log.Append($" {extension}: Version={OpenXRRuntime.GetExtensionVersion(extension)}\n");
|
||||
|
||||
DiagnosticReport.AddSectionEntry(section, "Runtime extensions enabled", log.ToString());
|
||||
}
|
||||
|
||||
[AOT.MonoPInvokeCallback(typeof(ReceiveNativeEventDelegate))]
|
||||
private static void ReceiveNativeEvent(OpenXRFeature.NativeEvent e, ulong payload)
|
||||
{
|
||||
var loader = Instance;
|
||||
|
||||
if (loader != null) loader.currentOpenXRState = e;
|
||||
|
||||
switch (e)
|
||||
{
|
||||
case OpenXRFeature.NativeEvent.XrRestartRequested:
|
||||
OpenXRRestarter.Instance.ShutdownAndRestart();
|
||||
break;
|
||||
|
||||
case OpenXRFeature.NativeEvent.XrReady:
|
||||
loader.StartInternal();
|
||||
break;
|
||||
|
||||
case OpenXRFeature.NativeEvent.XrBeginSession:
|
||||
loader.LogRequestedOpenXRFeatures();
|
||||
break;
|
||||
|
||||
case OpenXRFeature.NativeEvent.XrFocused:
|
||||
DiagnosticReport.DumpReport("System Startup Completed");
|
||||
break;
|
||||
|
||||
case OpenXRFeature.NativeEvent.XrRequestRestartLoop:
|
||||
Debug.Log("XR Initialization failed, will try to restart xr periodically.");
|
||||
OpenXRRestarter.Instance.PauseAndShutdownAndRestart();
|
||||
break;
|
||||
|
||||
case OpenXRFeature.NativeEvent.XrRequestGetSystemLoop:
|
||||
OpenXRRestarter.Instance.PauseAndRetryInitialization();
|
||||
break;
|
||||
|
||||
case OpenXRFeature.NativeEvent.XrStopping:
|
||||
loader.StopInternal();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
OpenXRFeature.ReceiveNativeEvent(e, payload);
|
||||
|
||||
if ((loader == null || !loader.isStarted) && e != OpenXRFeature.NativeEvent.XrInstanceChanged)
|
||||
return;
|
||||
|
||||
switch (e)
|
||||
{
|
||||
case OpenXRFeature.NativeEvent.XrExiting:
|
||||
OpenXRRestarter.Instance.Shutdown();
|
||||
break;
|
||||
|
||||
case OpenXRFeature.NativeEvent.XrLossPending:
|
||||
OpenXRRestarter.Instance.ShutdownAndRestart();
|
||||
break;
|
||||
|
||||
case OpenXRFeature.NativeEvent.XrInstanceLossPending:
|
||||
OpenXRRestarter.Instance.Shutdown();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal delegate void ReceiveNativeEventDelegate(OpenXRFeature.NativeEvent e, ulong payload);
|
||||
|
||||
internal static void RegisterOpenXRCallbacks()
|
||||
{
|
||||
Internal_SetCallbacks(ReceiveNativeEvent);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnAfterAssemblyReload()
|
||||
{
|
||||
AssemblyReloadEvents.afterAssemblyReload -= OnAfterAssemblyReload;
|
||||
|
||||
// Recreate the subsystems. Note that post domain reload this is more about
|
||||
// repopulating the subsystem instance map than it is about actually creating subsystems. At this
|
||||
// point the domain reload is finished and the SubsystemManager has already patched
|
||||
// all of the subsystem interop pointers so we just need to go out and get them again.
|
||||
CreateSubsystems();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
AppDomain currentDomain = AppDomain.CurrentDomain;
|
||||
|
||||
if (unhandledExceptionHandler != null)
|
||||
{
|
||||
currentDomain.UnhandledException -= unhandledExceptionHandler;
|
||||
unhandledExceptionHandler = null;
|
||||
}
|
||||
|
||||
unhandledExceptionHandler = new UnhandledExceptionEventHandler(ExceptionHandler);
|
||||
currentDomain.UnhandledException += unhandledExceptionHandler;
|
||||
|
||||
// If the loader is already initialized then this is likely due to a domain
|
||||
// reload so we need patch u the running instance reference.
|
||||
if (isInitialized && Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
// Recreate subsystems after all assemblies are finished loading. This cannot be done here
|
||||
// because the SubsystemManager is handling domain reload itself, but is called after
|
||||
// this call to OnEnable but before the afterAssemblyReload callback. The SubsystemManager will
|
||||
// reset the subsystem instance list on domain reload and thus if we create the subsystems now they
|
||||
// will be invalidated immediately after by the domain reload and the list will be out of sync. By waiting
|
||||
// until the afterAssemblyReload we can ensure the list has been created first.
|
||||
AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
|
||||
// Re-register the callbacks with the plugin to reflect the new class instance
|
||||
RegisterOpenXRCallbacks();
|
||||
|
||||
// Hook ourself back into onBeforeRender. While the onBeforeRender should no longer contain our
|
||||
// message loop hook we will remove it first just to be extra safe.
|
||||
Application.onBeforeRender -= ProcessOpenXRMessageLoop;
|
||||
Application.onBeforeRender += ProcessOpenXRMessageLoop;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.xr.openxr/Runtime/OpenXRLoader.cs.meta
Normal file
11
Packages/com.unity.xr.openxr/Runtime/OpenXRLoader.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3552e428dc7646a88de3ed3650f87da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
58
Packages/com.unity.xr.openxr/Runtime/OpenXRLoaderBase.cs
Normal file
58
Packages/com.unity.xr.openxr/Runtime/OpenXRLoaderBase.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
public partial class OpenXRLoaderBase
|
||||
{
|
||||
private const string LibraryName = "UnityOpenXR";
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "main_LoadOpenXRLibrary")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool Internal_LoadOpenXRLibrary(byte[] loaderPath);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "main_UnloadOpenXRLibrary")]
|
||||
internal static extern void Internal_UnloadOpenXRLibrary();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_SetCallbacks")]
|
||||
static extern void Internal_SetCallbacks(OpenXRLoader.ReceiveNativeEventDelegate callback);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_SetApplicationInfo", CharSet = CharSet.Ansi)]
|
||||
static extern void Internal_SetApplicationInfo(string applicationName, string applicationVersion, uint applicationVersionHash, string engineVersion);
|
||||
|
||||
// Session native imports
|
||||
[DllImport(LibraryName, EntryPoint = "session_RequestExitSession")]
|
||||
internal static extern void Internal_RequestExitSession();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "session_InitializeSession")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool Internal_InitializeSession();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "session_CreateSessionIfNeeded")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool Internal_CreateSessionIfNeeded();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "session_BeginSession")]
|
||||
internal static extern void Internal_BeginSession();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "session_EndSession")]
|
||||
internal static extern void Internal_EndSession();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "session_DestroySession")]
|
||||
internal static extern void Internal_DestroySession();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "messagepump_PumpMessageLoop")]
|
||||
static extern void Internal_PumpMessageLoop();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "session_SetSuccessfullyInitialized")]
|
||||
internal static extern void Internal_SetSuccessfullyInitialized([MarshalAs(UnmanagedType.I1)] bool value);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "unity_ext_RequestEnableExtensionString", CharSet = CharSet.Ansi)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool Internal_RequestEnableExtensionString(string extensionString);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "unity_ext_IsExtensionEnabled")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_IsExtensionEnabled(string extensionName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc82159cf04a86140a44a0172fa8784e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.Management;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
/// <summary>
|
||||
/// Loader for managing OpenXR devices that don't support PreInit.
|
||||
/// </summary>
|
||||
public class OpenXRLoaderNoPreInit : OpenXRLoaderBase
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee62ffd6c7e6f3545899783b1fbdd9d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1727
Packages/com.unity.xr.openxr/Runtime/OpenXRNativeTypes.cs
Normal file
1727
Packages/com.unity.xr.openxr/Runtime/OpenXRNativeTypes.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a56ab48cc5d2b854e9d6d96652e7465c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
502
Packages/com.unity.xr.openxr/Runtime/OpenXRProjectValidation.cs
Normal file
502
Packages/com.unity.xr.openxr/Runtime/OpenXRProjectValidation.cs
Normal file
@@ -0,0 +1,502 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.XR.Management;
|
||||
using UnityEditor.Build;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using UnityEngine.XR.OpenXR.Features.CompositionLayers;
|
||||
#endif
|
||||
|
||||
#if UNITY_RENDER_PIPELINES_UNIVERSAL
|
||||
using UnityEngine.Rendering.Universal;
|
||||
#endif // UNITY_RENDER_PIPELINES_UNIVERSAL
|
||||
|
||||
namespace UnityEditor.XR.OpenXR
|
||||
{
|
||||
/// <summary>
|
||||
/// OpenXR project validation details.
|
||||
/// </summary>
|
||||
public static class OpenXRProjectValidation
|
||||
{
|
||||
private static readonly OpenXRFeature.ValidationRule[] BuiltinValidationRules =
|
||||
{
|
||||
new OpenXRFeature.ValidationRule
|
||||
{
|
||||
message = "The OpenXR package has been updated and Unity must be restarted to complete the update.",
|
||||
checkPredicate = () => OpenXRSettings.Instance == null || (!OpenXRSettings.Instance.versionChanged),
|
||||
fixIt = RequireRestart,
|
||||
error = true,
|
||||
errorEnteringPlaymode = true,
|
||||
buildTargetGroup = BuildTargetGroup.Standalone,
|
||||
},
|
||||
new OpenXRFeature.ValidationRule
|
||||
{
|
||||
message = "The OpenXR Package Settings asset has duplicate settings and must be regenerated.",
|
||||
checkPredicate = AssetHasNoDuplicates,
|
||||
fixIt = RegenerateXRPackageSettingsAsset,
|
||||
error = false,
|
||||
errorEnteringPlaymode = false
|
||||
},
|
||||
new OpenXRFeature.ValidationRule()
|
||||
{
|
||||
message = "Gamma Color Space is not supported when using OpenGLES.",
|
||||
checkPredicate = () =>
|
||||
{
|
||||
if (PlayerSettings.colorSpace == ColorSpace.Linear)
|
||||
return true;
|
||||
|
||||
return !Enum.GetValues(typeof(BuildTarget))
|
||||
.Cast<BuildTarget>()
|
||||
.Where(t =>
|
||||
{
|
||||
var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(t);
|
||||
if (!BuildPipeline.IsBuildTargetSupported(buildTargetGroup, t))
|
||||
return false;
|
||||
|
||||
var settings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(buildTargetGroup);
|
||||
if (null == settings)
|
||||
return false;
|
||||
|
||||
var manager = settings.Manager;
|
||||
if (null == manager)
|
||||
return false;
|
||||
|
||||
return manager.activeLoaders.OfType<OpenXRLoader>().Any();
|
||||
})
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
.Any(buildTarget => PlayerSettings.GetGraphicsAPIs(buildTarget).Any(g => g == GraphicsDeviceType.OpenGLES3));
|
||||
#else
|
||||
// Keeping OpenGL ES 2 support for 2022 and older versions.
|
||||
.Any(buildTarget => PlayerSettings.GetGraphicsAPIs(buildTarget).Any(g => g == GraphicsDeviceType.OpenGLES2 || g == GraphicsDeviceType.OpenGLES3));
|
||||
#endif
|
||||
},
|
||||
fixIt = () => PlayerSettings.colorSpace = ColorSpace.Linear,
|
||||
fixItMessage = "Set PlayerSettings.colorSpace to ColorSpace.Linear",
|
||||
error = true,
|
||||
errorEnteringPlaymode = true,
|
||||
buildTargetGroup = BuildTargetGroup.Android,
|
||||
},
|
||||
new OpenXRFeature.ValidationRule()
|
||||
{
|
||||
message = "At least one interaction profile must be added. Please select which controllers you will be testing against in the Features menu.",
|
||||
checkPredicate = () =>
|
||||
{
|
||||
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
|
||||
return settings == null || settings.GetFeatures<OpenXRInteractionFeature>().Any(f => f.enabled);
|
||||
},
|
||||
fixIt = OpenProjectSettings,
|
||||
fixItAutomatic = false,
|
||||
fixItMessage = "Open Project Settings to select one or more interaction profiles.",
|
||||
highlighterFocus = new OpenXRFeature.ValidationRule.HighlighterFocusData
|
||||
{
|
||||
searchText = "Enabled Interaction Profiles",
|
||||
windowTitle = "Project Settings",
|
||||
}
|
||||
},
|
||||
new OpenXRFeature.ValidationRule()
|
||||
{
|
||||
message = "Only arm64 or x86_x64 is supported on Android with OpenXR. Other architectures are not supported.",
|
||||
checkPredicate = () => (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android) || ((PlayerSettings.Android.targetArchitectures & (~(AndroidArchitecture.ARM64 | AndroidArchitecture.X86_64))) == 0) && (PlayerSettings.Android.targetArchitectures != AndroidArchitecture.None),
|
||||
fixIt = () =>
|
||||
{
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
PlayerSettings.SetScriptingBackend(NamedBuildTarget.Android, ScriptingImplementation.IL2CPP);
|
||||
#else
|
||||
PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP);
|
||||
#endif
|
||||
PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64;
|
||||
},
|
||||
fixItMessage = "Change android build to arm64 or x86_64 and enable il2cpp.",
|
||||
error = true,
|
||||
buildTargetGroup = BuildTargetGroup.Android,
|
||||
},
|
||||
new OpenXRFeature.ValidationRule()
|
||||
{
|
||||
message = "The only standalone targets supported are Windows x64 and OSX with OpenXR. Other architectures and operating systems are not supported at this time.",
|
||||
checkPredicate = () => (BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget) != BuildTargetGroup.Standalone) || (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64) || (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneOSX),
|
||||
fixIt = () => EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows64),
|
||||
fixItMessage = "Switch active build target to StandaloneWindows64.",
|
||||
error = true,
|
||||
errorEnteringPlaymode = true,
|
||||
buildTargetGroup = BuildTargetGroup.Standalone,
|
||||
},
|
||||
new OpenXRFeature.ValidationRule()
|
||||
{
|
||||
message = "The project's minimum Android API level is lower than 24, which is the lowest level supported by the OpenXR plug-in.",
|
||||
helpText = "This API level is required by the OpenXR plug-in's loader library. If your project is using a custom loader library, ensure that the project's minimum API level is supported by your library, which may be lower than 24.",
|
||||
checkPredicate = () => PlayerSettings.Android.minSdkVersion >= AndroidSdkVersions.AndroidApiLevel24,
|
||||
fixIt = () => PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel24,
|
||||
fixItMessage = "Set Player Settings minimum Android API Level to 24.",
|
||||
buildTargetGroup = BuildTargetGroup.Android,
|
||||
},
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
new OpenXRFeature.ValidationRule()
|
||||
{
|
||||
message = "Lock Input to Game View in order for tracked pose driver to work in editor playmode.",
|
||||
checkPredicate = () =>
|
||||
{
|
||||
var cls = typeof(UnityEngine.InputSystem.InputDevice).Assembly.GetType("UnityEngine.InputSystem.Editor.InputEditorUserSettings");
|
||||
if (cls == null) return true;
|
||||
var prop = cls.GetProperty("lockInputToGameView", BindingFlags.Static | BindingFlags.Public);
|
||||
if (prop == null) return true;
|
||||
return (bool)prop.GetValue(null);
|
||||
},
|
||||
fixItMessage = "Enables the 'Lock Input to Game View' setting in Window -> Analysis -> Input Debugger -> Options",
|
||||
fixIt = () =>
|
||||
{
|
||||
var cls = typeof(UnityEngine.InputSystem.InputDevice).Assembly.GetType("UnityEngine.InputSystem.Editor.InputEditorUserSettings");
|
||||
if (cls == null) return;
|
||||
var prop = cls.GetProperty("lockInputToGameView", BindingFlags.Static | BindingFlags.Public);
|
||||
if (prop == null) return;
|
||||
prop.SetValue(null, true);
|
||||
},
|
||||
errorEnteringPlaymode = true,
|
||||
buildTargetGroup = BuildTargetGroup.Standalone,
|
||||
},
|
||||
#endif // ENABLE_INPUT_SYSTEM
|
||||
#if UNITY_RENDER_PIPELINES_UNIVERSAL
|
||||
new OpenXRFeature.ValidationRule() {
|
||||
message = "[Optional] Enabling URP upscaling decreases performance significantly because it is currently not supported by XR. Please disable it by setting Upscaling Filter to Automatic in the Universal Render Pipeline Asset.",
|
||||
checkPredicate = URPUpscalingValidationPredicate,
|
||||
fixIt = URPUpscalingFix,
|
||||
fixItMessage = "URP upscaling has been disabled by setting the Upscaling Filter to Automatic in the Universal Render Pipeline Asset.",
|
||||
error = false,
|
||||
errorEnteringPlaymode = false,
|
||||
},
|
||||
#endif
|
||||
new OpenXRFeature.ValidationRule()
|
||||
{
|
||||
message = "[Optional] Switch to use InputSystem.XR.PoseControl instead of OpenXR.Input.PoseControl, which will be deprecated in a future release.",
|
||||
checkPredicate = () =>
|
||||
{
|
||||
#if !USE_INPUT_SYSTEM_POSE_CONTROL && INPUT_SYSTEM_POSE_VALID
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
},
|
||||
fixIt = EnableInputSystemPoseControlDefine,
|
||||
error = false,
|
||||
errorEnteringPlaymode = false,
|
||||
},
|
||||
new OpenXRFeature.ValidationRule()
|
||||
{
|
||||
message = "[Optional] Switch to use StickControl thumbsticks instead of Vector2Control, but may break existing projects that have code dependencies to the Vector2Control type. StickControl allows more input options for thumbstick-based control, such as acting as both a combined 2D vector, two independent axes or a four-way Dpad with 4 independent buttons.",
|
||||
checkPredicate = () =>
|
||||
{
|
||||
#if USE_STICK_CONTROL_THUMBSTICKS
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
},
|
||||
fixIt = EnableStickControlThumbsticksDefine,
|
||||
error = false,
|
||||
errorEnteringPlaymode = false,
|
||||
},
|
||||
new OpenXRFeature.ValidationRule()
|
||||
{
|
||||
message = "[Optional] Soft shadows can negatively impact performance on HoloLens, disabling soft shadows is recommended",
|
||||
checkPredicate = SoftShadowValidationPredicate,
|
||||
fixItMessage =
|
||||
@"When using the Built-In Render Pipeline, enable hard shadows only.
|
||||
|
||||
When using the Universal Render Pipeline, open the Render Pipeline Asset in Editor for modification.",
|
||||
fixIt = SoftShadowFixItButtonPress,
|
||||
error = false,
|
||||
buildTargetGroup = BuildTargetGroup.WSA
|
||||
},
|
||||
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
new OpenXRFeature.ValidationRule()
|
||||
{
|
||||
message = $"The <b>{OpenXRCompositionLayersFeature.FeatureName}</b> feature is required to use the Composition Layers package.",
|
||||
checkPredicate = () =>
|
||||
{
|
||||
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
|
||||
return settings == null || settings.GetFeatures<OpenXRCompositionLayersFeature>().Any(f => f.enabled);
|
||||
},
|
||||
fixItMessage =
|
||||
$"Go to <b>Project Settings</b> > <b>XR Plug-in Management</b> > <b>OpenXR</b> > <b>{EditorUserBuildSettings.selectedBuildTargetGroup}</b> tab. In the list of OpenXR Features, enable <b>{OpenXRCompositionLayersFeature.FeatureName}</b>.",
|
||||
fixIt = () =>
|
||||
{
|
||||
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
|
||||
var feature = settings.GetFeature<OpenXRCompositionLayersFeature>();
|
||||
feature.enabled = true;
|
||||
},
|
||||
error = true,
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
private static readonly List<OpenXRFeature.ValidationRule> CachedValidationList = new List<OpenXRFeature.ValidationRule>(BuiltinValidationRules.Length);
|
||||
|
||||
internal static void EnableInputSystemPoseControlDefine()
|
||||
{
|
||||
AddDefineToBuildTarget("USE_INPUT_SYSTEM_POSE_CONTROL");
|
||||
}
|
||||
|
||||
internal static void EnableStickControlThumbsticksDefine()
|
||||
{
|
||||
AddDefineToBuildTarget("USE_STICK_CONTROL_THUMBSTICKS");
|
||||
}
|
||||
|
||||
private static void AddDefineToBuildTarget(string defineName)
|
||||
{
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
NamedBuildTarget[] targets = { NamedBuildTarget.Android, NamedBuildTarget.Standalone, NamedBuildTarget.WindowsStoreApps };
|
||||
for (var index = 0; index < targets.Length; index++)
|
||||
{
|
||||
var defines = PlayerSettings.GetScriptingDefineSymbols(targets[index]);
|
||||
defines += $";{defineName}";
|
||||
PlayerSettings.SetScriptingDefineSymbols(targets[index], defines);
|
||||
}
|
||||
|
||||
#else
|
||||
BuildTargetGroup[] buildTargets = {BuildTargetGroup.Android, BuildTargetGroup.Standalone, BuildTargetGroup.WSA};
|
||||
for (var index = 0; index < buildTargets.Length; index++)
|
||||
{
|
||||
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargets[index]);
|
||||
defines += $";{defineName}";
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargets[index], defines);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static bool AssetHasNoDuplicates()
|
||||
{
|
||||
var packageSettings = OpenXRSettings.GetPackageSettings();
|
||||
if (packageSettings == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string path = packageSettings.PackageSettingsAssetPath();
|
||||
var loadedAssets = AssetDatabase.LoadAllAssetsAtPath(path);
|
||||
if (loadedAssets == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for duplicate "full" feature name (Feature class type name + Feature name (contains BuildTargetGroup))
|
||||
HashSet<string> fullFeatureNames = new HashSet<string>();
|
||||
foreach (var loadedAsset in loadedAssets)
|
||||
{
|
||||
OpenXRFeature individualFeature = loadedAsset as OpenXRFeature;
|
||||
if (individualFeature != null)
|
||||
{
|
||||
Type type = individualFeature.GetType();
|
||||
string featureName = individualFeature.name;
|
||||
string fullFeatureName = type.FullName + "\\" + featureName;
|
||||
|
||||
if (fullFeatureNames.Contains(fullFeatureName))
|
||||
return false;
|
||||
fullFeatureNames.Add(fullFeatureName);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void RegenerateXRPackageSettingsAsset()
|
||||
{
|
||||
// Deleting the OpenXR PackageSettings asset also destroys the OpenXRPackageSettings object.
|
||||
// Need to get the static method to create the new asset and object before deleting the asset.
|
||||
var packageSettings = OpenXRSettings.GetPackageSettings();
|
||||
if (packageSettings == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string path = packageSettings.PackageSettingsAssetPath();
|
||||
|
||||
Action createAssetCallback = packageSettings.RefreshFeatureSets;
|
||||
AssetDatabase.DeleteAsset(path);
|
||||
|
||||
EditorBuildSettings.RemoveConfigObject(Constants.k_SettingsKey);
|
||||
|
||||
createAssetCallback();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the OpenXR project settings
|
||||
/// </summary>
|
||||
internal static void OpenProjectSettings() => SettingsService.OpenProjectSettings("Project/XR Plug-in Management/OpenXR");
|
||||
|
||||
internal static void GetAllValidationIssues(List<OpenXRFeature.ValidationRule> issues, BuildTargetGroup buildTargetGroup)
|
||||
{
|
||||
issues.Clear();
|
||||
issues.AddRange(BuiltinValidationRules.Where(s => s.buildTargetGroup == buildTargetGroup || s.buildTargetGroup == BuildTargetGroup.Unknown));
|
||||
OpenXRFeature.GetFullValidationList(issues, buildTargetGroup);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gathers and evaluates validation issues and adds them to a list.
|
||||
/// </summary>
|
||||
/// <param name="issues">List of validation issues to populate. List is cleared before populating.</param>
|
||||
/// <param name="buildTarget">Build target group to check for validation issues</param>
|
||||
public static void GetCurrentValidationIssues(List<OpenXRFeature.ValidationRule> issues, BuildTargetGroup buildTargetGroup)
|
||||
{
|
||||
CachedValidationList.Clear();
|
||||
CachedValidationList.AddRange(BuiltinValidationRules.Where(s => s.buildTargetGroup == buildTargetGroup || s.buildTargetGroup == BuildTargetGroup.Unknown));
|
||||
OpenXRFeature.GetValidationList(CachedValidationList, buildTargetGroup);
|
||||
|
||||
issues.Clear();
|
||||
foreach (var validation in CachedValidationList)
|
||||
{
|
||||
if (!validation.checkPredicate?.Invoke() ?? false)
|
||||
{
|
||||
issues.Add(validation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs validation issues to console.
|
||||
/// </summary>
|
||||
/// <param name="targetGroup"></param>
|
||||
/// <returns>true if there were any errors that should stop the build</returns>
|
||||
internal static bool LogBuildValidationIssues(BuildTargetGroup targetGroup)
|
||||
{
|
||||
var failures = new List<OpenXRFeature.ValidationRule>();
|
||||
GetCurrentValidationIssues(failures, targetGroup);
|
||||
|
||||
if (failures.Count <= 0) return false;
|
||||
|
||||
bool anyErrors = false;
|
||||
foreach (var result in failures)
|
||||
{
|
||||
if (result.error)
|
||||
Debug.LogError(result.message);
|
||||
else
|
||||
Debug.LogWarning(result.message);
|
||||
anyErrors |= result.error;
|
||||
}
|
||||
|
||||
if (anyErrors)
|
||||
{
|
||||
Debug.LogError("Double click to fix OpenXR Project Validation Issues.");
|
||||
}
|
||||
|
||||
return anyErrors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs playmode validation issues (anything rule that fails with errorEnteringPlaymode set to true).
|
||||
/// </summary>
|
||||
/// <returns>true if there were any errors that should prevent openxr starting in editor playmode</returns>
|
||||
internal static bool LogPlaymodeValidationIssues()
|
||||
{
|
||||
var failures = new List<OpenXRFeature.ValidationRule>();
|
||||
GetCurrentValidationIssues(failures, BuildTargetGroup.Standalone);
|
||||
|
||||
if (failures.Count <= 0) return false;
|
||||
|
||||
bool playmodeErrors = false;
|
||||
foreach (var result in failures)
|
||||
{
|
||||
if (result.errorEnteringPlaymode)
|
||||
Debug.LogError(result.message);
|
||||
playmodeErrors |= result.errorEnteringPlaymode;
|
||||
}
|
||||
|
||||
return playmodeErrors;
|
||||
}
|
||||
|
||||
private static void RequireRestart()
|
||||
{
|
||||
// There is no public way to change the input handling backend .. so resorting to non-public way for now.
|
||||
if (!EditorUtility.DisplayDialog("Unity editor restart required", "The Unity editor must be restarted for this change to take effect. Cancel to revert changes.", "Apply", "Cancel"))
|
||||
return;
|
||||
|
||||
typeof(EditorApplication).GetMethod("RestartEditorAndRecompileScripts", BindingFlags.NonPublic | BindingFlags.Static)?.Invoke(null, null);
|
||||
}
|
||||
|
||||
private static bool SoftShadowValidationPredicate()
|
||||
{
|
||||
RenderPipelineAsset currentRenderPipelineAsset = GraphicsSettings.currentRenderPipeline;
|
||||
// If current render pipeline is Built-In Render Pipeline
|
||||
if (currentRenderPipelineAsset == null)
|
||||
return QualitySettings.shadows != UnityEngine.ShadowQuality.All;
|
||||
#if UNITY_RENDER_PIPELINES_UNIVERSAL
|
||||
UniversalRenderPipelineAsset urpAsset = currentRenderPipelineAsset as UniversalRenderPipelineAsset;
|
||||
if (urpAsset != null)
|
||||
return urpAsset.supportsSoftShadows == false;
|
||||
#endif // UNITY_RENDER_PIPELINES_UNIVERSAL
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool URPUpscalingValidationPredicate()
|
||||
{
|
||||
RenderPipelineAsset currentRenderPipelineAsset = GraphicsSettings.currentRenderPipeline;
|
||||
if (currentRenderPipelineAsset == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#if UNITY_RENDER_PIPELINES_UNIVERSAL
|
||||
UniversalRenderPipelineAsset urpAsset = currentRenderPipelineAsset as UniversalRenderPipelineAsset;
|
||||
if (urpAsset != null) {
|
||||
return urpAsset.upscalingFilter == UpscalingFilterSelection.Auto;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void SoftShadowFixItButtonPress()
|
||||
{
|
||||
RenderPipelineAsset currentRenderPipelineAsset = GraphicsSettings.currentRenderPipeline;
|
||||
// If current render pipeline is Built-In Render Pipeline
|
||||
if (currentRenderPipelineAsset == null)
|
||||
{
|
||||
QualitySettings.shadows = UnityEngine.ShadowQuality.HardOnly;
|
||||
return;
|
||||
}
|
||||
#if UNITY_RENDER_PIPELINES_UNIVERSAL
|
||||
UniversalRenderPipelineAsset urpAsset = currentRenderPipelineAsset as UniversalRenderPipelineAsset;
|
||||
if (urpAsset != null)
|
||||
{
|
||||
var urpAssetID = urpAsset.GetInstanceID();
|
||||
if (AssetDatabase.CanOpenAssetInEditor(urpAssetID))
|
||||
AssetDatabase.OpenAsset(urpAssetID);
|
||||
else
|
||||
Debug.LogWarning("Unable to open URP asset in Editor.");
|
||||
return;
|
||||
}
|
||||
#endif // UNITY_RENDER_PIPELINES_UNIVERSAL
|
||||
Debug.LogWarning("Unable to disable soft shadows.");
|
||||
}
|
||||
private static void URPUpscalingFix()
|
||||
{
|
||||
RenderPipelineAsset currentRenderPipelineAsset = GraphicsSettings.currentRenderPipeline;
|
||||
// If current render pipeline is Built-In Render Pipeline
|
||||
if (currentRenderPipelineAsset == null)
|
||||
{
|
||||
Debug.LogWarning("No Render Pipeline Asset was found. Please assign a Default Render Pipeline Asset in the Graphics settings or assign a Render Pipeline Asset in the active Quality level.");
|
||||
return;
|
||||
}
|
||||
#if UNITY_RENDER_PIPELINES_UNIVERSAL
|
||||
UniversalRenderPipelineAsset urpAsset = currentRenderPipelineAsset as UniversalRenderPipelineAsset;
|
||||
if (urpAsset != null)
|
||||
{
|
||||
var urpAssetID = urpAsset.GetInstanceID();
|
||||
if (AssetDatabase.CanOpenAssetInEditor(urpAssetID))
|
||||
{
|
||||
AssetDatabase.OpenAsset(urpAssetID);
|
||||
urpAsset.upscalingFilter = UpscalingFilterSelection.Auto;
|
||||
}
|
||||
else
|
||||
Debug.LogWarning("Unable to open URP asset in Editor.");
|
||||
return;
|
||||
}
|
||||
#endif // UNITY_RENDER_PIPELINES_UNIVERSAL
|
||||
Debug.LogWarning("Unable to disable URP upscaling.");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad4f6b0c01997fa44844397ca09a5544
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
429
Packages/com.unity.xr.openxr/Runtime/OpenXRRenderSettings.cs
Normal file
429
Packages/com.unity.xr.openxr/Runtime/OpenXRRenderSettings.cs
Normal file
@@ -0,0 +1,429 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Android;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
public partial class OpenXRSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Stereo rendering mode.
|
||||
/// </summary>
|
||||
public enum RenderMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Submit separate draw calls for each eye.
|
||||
/// </summary>
|
||||
MultiPass,
|
||||
|
||||
/// <summary>
|
||||
/// Submit one draw call for both eyes.
|
||||
/// </summary>
|
||||
SinglePassInstanced,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Stereo rendering mode.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
private RenderMode m_renderMode = RenderMode.SinglePassInstanced;
|
||||
|
||||
/// <summary>
|
||||
/// Runtime Stereo rendering mode.
|
||||
/// </summary>
|
||||
public RenderMode renderMode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
return Internal_GetRenderMode();
|
||||
else
|
||||
return m_renderMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
Internal_SetRenderMode(value);
|
||||
else
|
||||
m_renderMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool m_autoColorSubmissionMode = true;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically select the default color submission mode.
|
||||
/// </summary>
|
||||
public bool autoColorSubmissionMode
|
||||
{
|
||||
get => m_autoColorSubmissionMode;
|
||||
set => m_autoColorSubmissionMode = value;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private ColorSubmissionModeList m_colorSubmissionModes = new ColorSubmissionModeList();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the preference for what color format the OpenXR display swapchain should use.
|
||||
/// </summary>
|
||||
public ColorSubmissionModeGroup[] colorSubmissionModes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_autoColorSubmissionMode)
|
||||
return new[] { OpenXRSettings.kDefaultColorMode };
|
||||
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
{
|
||||
int arraySize = Internal_GetColorSubmissionModes(null, 0);
|
||||
int[] intModes = new int[arraySize];
|
||||
Internal_GetColorSubmissionModes(intModes, arraySize);
|
||||
return intModes.Select(i => (ColorSubmissionModeGroup)i).ToArray();
|
||||
}
|
||||
else
|
||||
return m_colorSubmissionModes.m_List;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
Internal_SetColorSubmissionModes(
|
||||
value.Select(e => (int)e).ToArray(),
|
||||
value.Length
|
||||
);
|
||||
else
|
||||
m_colorSubmissionModes.m_List = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runtime Depth submission mode.
|
||||
/// </summary>
|
||||
public enum DepthSubmissionMode
|
||||
{
|
||||
/// <summary>
|
||||
/// No depth is submitted to the OpenXR compositor.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// 16-bit depth is submitted to the OpenXR compositor.
|
||||
/// </summary>
|
||||
Depth16Bit,
|
||||
|
||||
/// <summary>
|
||||
/// 24-bit depth is submitted to the OpenXR compositor.
|
||||
/// </summary>
|
||||
Depth24Bit,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Different Internal Unity APIs to use in the backend.
|
||||
/// </summary>
|
||||
public enum BackendFovationApi : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the legacy Built-in API for Foveation. This is the only option for Built-in.
|
||||
/// </summary>
|
||||
Legacy = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Use the Foveation for SRP API.
|
||||
/// </summary>
|
||||
SRPFoveation = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Space Warp motion vector texture format.
|
||||
/// </summary>
|
||||
public enum SpaceWarpMotionVectorTextureFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// RGBA16f texture format
|
||||
/// </summary>
|
||||
RGBA16f,
|
||||
|
||||
/// <summary>
|
||||
/// RG16f texture format
|
||||
/// </summary>
|
||||
RG16f,
|
||||
}
|
||||
|
||||
#if UNITY_ANDROID
|
||||
|
||||
private string[] m_eyeTrackingPermissionsToRequest = new string[]
|
||||
{
|
||||
"com.oculus.permission.EYE_TRACKING",
|
||||
"android.permission.EYE_TRACKING",
|
||||
"android.permission.EYE_TRACKING_FINE"
|
||||
};
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Enables XR_KHR_composition_layer_depth if possible and resolves or submits depth to OpenXR runtime.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
private DepthSubmissionMode m_depthSubmissionMode = DepthSubmissionMode.None;
|
||||
|
||||
static void PermissionGrantedCallback(string permissionName)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
foreach (var permission in GetInstance(true).m_eyeTrackingPermissionsToRequest)
|
||||
{
|
||||
if (permissionName == permission)
|
||||
{
|
||||
Internal_SetHasEyeTrackingPermissions(true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables XR_KHR_composition_layer_depth if possible and resolves or submits depth to OpenXR runtime.
|
||||
/// </summary>
|
||||
public DepthSubmissionMode depthSubmissionMode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
return Internal_GetDepthSubmissionMode();
|
||||
else
|
||||
return m_depthSubmissionMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
Internal_SetDepthSubmissionMode(value);
|
||||
else
|
||||
m_depthSubmissionMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Space Warp motion vector texture format (default is RGBA16f).
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
private SpaceWarpMotionVectorTextureFormat m_spacewarpMotionVectorTextureFormat = SpaceWarpMotionVectorTextureFormat.RGBA16f;
|
||||
|
||||
/// <summary>
|
||||
/// Selects the motion vector texture format for Space Warp.
|
||||
/// </summary>
|
||||
public SpaceWarpMotionVectorTextureFormat spacewarpMotionVectorTextureFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
return Internal_GetSpaceWarpMotionVectorTextureFormat();
|
||||
else
|
||||
return m_spacewarpMotionVectorTextureFormat;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
Internal_SetSpaceWarpMotionVectorTextureFormat(value);
|
||||
else
|
||||
m_spacewarpMotionVectorTextureFormat = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool m_optimizeBufferDiscards = false;
|
||||
|
||||
/// <summary>
|
||||
/// Optimization that allows 4x MSAA textures to be memoryless on Vulkan
|
||||
/// </summary>
|
||||
public bool optimizeBufferDiscards
|
||||
{
|
||||
get { return m_optimizeBufferDiscards; }
|
||||
set
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
Internal_SetOptimizeBufferDiscards(value);
|
||||
else
|
||||
m_optimizeBufferDiscards = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyRenderSettings()
|
||||
{
|
||||
Internal_SetSymmetricProjection(m_symmetricProjection);
|
||||
#if UNITY_6000_1_OR_NEWER
|
||||
Internal_SetOptimizeMultiviewRenderRegions(m_optimizeMultiviewRenderRegions);
|
||||
#endif
|
||||
#if UNITY_2023_2_OR_NEWER
|
||||
Internal_SetUsedFoveatedRenderingApi(m_foveatedRenderingApi);
|
||||
#endif
|
||||
Internal_SetRenderMode(m_renderMode);
|
||||
Internal_SetColorSubmissionModes(
|
||||
m_colorSubmissionModes.m_List.Select(e => (int)e).ToArray(),
|
||||
m_colorSubmissionModes.m_List.Length
|
||||
);
|
||||
Internal_SetDepthSubmissionMode(m_depthSubmissionMode);
|
||||
Internal_SetSpaceWarpMotionVectorTextureFormat(m_spacewarpMotionVectorTextureFormat);
|
||||
Internal_SetOptimizeBufferDiscards(m_optimizeBufferDiscards);
|
||||
|
||||
#if UNITY_ANDROID // Only Android need specific permissions for eye tracking, for now
|
||||
var foveatedRenderingFreature = GetFeature<FoveatedRenderingFeature>();
|
||||
if (m_eyeTrackingPermissionsToRequest != null && m_eyeTrackingPermissionsToRequest.Length > 0
|
||||
&& (Internal_HasRequestedEyeTrackingPermissions()
|
||||
|| (foveatedRenderingFreature != null && foveatedRenderingFreature.enabled)))
|
||||
{
|
||||
var permissionCallbacks = new PermissionCallbacks();
|
||||
permissionCallbacks.PermissionGranted += PermissionGrantedCallback;
|
||||
Permission.RequestUserPermissions(m_eyeTrackingPermissionsToRequest, permissionCallbacks);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
[SerializeField]
|
||||
private bool m_symmetricProjection = false;
|
||||
|
||||
#if UNITY_6000_1_OR_NEWER
|
||||
[SerializeField]
|
||||
private bool m_optimizeMultiviewRenderRegions = false;
|
||||
#endif
|
||||
|
||||
#if UNITY_2023_2_OR_NEWER
|
||||
[SerializeField]
|
||||
private BackendFovationApi m_foveatedRenderingApi = BackendFovationApi.Legacy;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, when the application begins it will create a stereo symmetric view that has the eye buffer resolution change based on the IPD.
|
||||
/// Provides a performance benefit across all IPDs.
|
||||
/// </summary>
|
||||
public bool symmetricProjection
|
||||
{
|
||||
get { return m_symmetricProjection; }
|
||||
set
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
Internal_SetSymmetricProjection(value);
|
||||
else
|
||||
m_symmetricProjection = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_6000_1_OR_NEWER
|
||||
/// <summary>
|
||||
/// Activates Multiview Render Regions optimizations at application start.
|
||||
/// Requires Vulkan as the Graphics API, Render Mode set to Multi-view and Symmetric rendering enabled.
|
||||
/// </summary>
|
||||
public bool optimizeMultiviewRenderRegions
|
||||
{
|
||||
get { return m_optimizeMultiviewRenderRegions; }
|
||||
set
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
Internal_SetOptimizeMultiviewRenderRegions(value);
|
||||
else
|
||||
m_optimizeMultiviewRenderRegions = value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Different APIs to use in the backend.
|
||||
/// On Built-in Render Pipeline, only Legacy will be used.
|
||||
/// On Scriptable Render Pipelines, it is highly recommended to use the SRPFoveation API. More textures will use FDM with the SRPFoveation API.
|
||||
/// </summary>
|
||||
#if UNITY_2023_2_OR_NEWER
|
||||
public BackendFovationApi foveatedRenderingApi
|
||||
{
|
||||
get
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
return Internal_GetUsedFoveatedRenderingApi();
|
||||
else
|
||||
return m_foveatedRenderingApi;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
Internal_SetUsedFoveatedRenderingApi(value);
|
||||
else
|
||||
m_foveatedRenderingApi = value;
|
||||
}
|
||||
}
|
||||
#else
|
||||
public BackendFovationApi foveatedRenderingApi => BackendFovationApi.Legacy;
|
||||
#endif
|
||||
|
||||
private const string LibraryName = "UnityOpenXR";
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_SetRenderMode")]
|
||||
private static extern void Internal_SetRenderMode(RenderMode renderMode);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetRenderMode")]
|
||||
private static extern RenderMode Internal_GetRenderMode();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_SetDepthSubmissionMode")]
|
||||
private static extern void Internal_SetDepthSubmissionMode(
|
||||
DepthSubmissionMode depthSubmissionMode
|
||||
);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetDepthSubmissionMode")]
|
||||
private static extern DepthSubmissionMode Internal_GetDepthSubmissionMode();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_SetSpaceWarpMotionVectorTextureFormat")]
|
||||
private static extern void Internal_SetSpaceWarpMotionVectorTextureFormat(
|
||||
SpaceWarpMotionVectorTextureFormat spaceWarpMotionVectorTextureFormat
|
||||
);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetSpaceWarpMotionVectorTextureFormat")]
|
||||
private static extern SpaceWarpMotionVectorTextureFormat Internal_GetSpaceWarpMotionVectorTextureFormat();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_SetSymmetricProjection")]
|
||||
private static extern void Internal_SetSymmetricProjection([MarshalAs(UnmanagedType.I1)] bool enabled);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_SetOptimizeMultiviewRenderRegions")]
|
||||
private static extern void Internal_SetOptimizeMultiviewRenderRegions([MarshalAs(UnmanagedType.I1)] bool enabled);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_SetOptimizeBufferDiscards")]
|
||||
private static extern void Internal_SetOptimizeBufferDiscards([MarshalAs(UnmanagedType.I1)] bool enabled);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "OculusFoveation_SetUsedApi")]
|
||||
private static extern void Internal_SetUsedFoveatedRenderingApi(BackendFovationApi api);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "OculusFoveation_GetUsedApi")]
|
||||
internal static extern BackendFovationApi Internal_GetUsedFoveatedRenderingApi();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "OculusFoveation_HasRequestedEyeTrackingPermissions")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool Internal_HasRequestedEyeTrackingPermissions();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "OculusFoveation_GetHasEyeTrackingPermissions")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool Internal_GetHasEyeTrackingPermissions();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "OculusFoveation_SetHasEyeTrackingPermissions")]
|
||||
internal static extern void Internal_SetHasEyeTrackingPermissions([MarshalAs(UnmanagedType.I1)] bool value);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_SetColorSubmissionMode")]
|
||||
private static extern void Internal_SetColorSubmissionMode(
|
||||
ColorSubmissionModeGroup[] colorSubmissionMode
|
||||
);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_SetColorSubmissionModes")]
|
||||
private static extern void Internal_SetColorSubmissionModes(
|
||||
int[] colorSubmissionMode,
|
||||
int arraySize
|
||||
);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetColorSubmissionModes")]
|
||||
private static extern int Internal_GetColorSubmissionModes(
|
||||
[Out] int[] colorSubmissionMode,
|
||||
int arraySize
|
||||
);
|
||||
|
||||
[DllImport("UnityOpenXR", EntryPoint = "NativeConfig_GetIsUsingLegacyXRDisplay")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetIsUsingLegacyXRDisplay();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06194331529372048ab65138f86d65f8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
291
Packages/com.unity.xr.openxr/Runtime/OpenXRRestarter.cs
Normal file
291
Packages/com.unity.xr.openxr/Runtime/OpenXRRestarter.cs
Normal file
@@ -0,0 +1,291 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.Management;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
internal class OpenXRRestarter : MonoBehaviour
|
||||
{
|
||||
internal Action onAfterRestart;
|
||||
internal Action onAfterShutdown;
|
||||
internal Action onQuit;
|
||||
internal Action onAfterCoroutine;
|
||||
internal Action onAfterSuccessfulRestart;
|
||||
|
||||
static OpenXRRestarter()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorApplication.playModeStateChanged += (state) =>
|
||||
{
|
||||
if (state == PlayModeStateChange.EnteredPlayMode)
|
||||
m_pauseAndRestartAttempts = 0;
|
||||
};
|
||||
#endif
|
||||
TimeBetweenRestartAttempts = 5.0f;
|
||||
DisableApplicationQuit = false;
|
||||
}
|
||||
|
||||
public void ResetCallbacks()
|
||||
{
|
||||
onAfterRestart = null;
|
||||
onAfterSuccessfulRestart = null;
|
||||
onAfterShutdown = null;
|
||||
onAfterCoroutine = null;
|
||||
onQuit = null;
|
||||
m_pauseAndRestartAttempts = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the restarter is currently running
|
||||
/// </summary>
|
||||
public bool isRunning => m_Coroutine != null;
|
||||
|
||||
private static OpenXRRestarter s_Instance = null;
|
||||
|
||||
private Coroutine m_Coroutine;
|
||||
|
||||
private static int m_pauseAndRestartCoroutineCount = 0;
|
||||
|
||||
private Object m_PauseAndRestartCoroutineCountLock = new Object();
|
||||
|
||||
private static int m_pauseAndRestartAttempts = 0;
|
||||
|
||||
public static float TimeBetweenRestartAttempts
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public static int PauseAndRestartAttempts
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_pauseAndRestartAttempts;
|
||||
}
|
||||
}
|
||||
|
||||
internal static int PauseAndRestartCoroutineCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_pauseAndRestartCoroutineCount;
|
||||
}
|
||||
}
|
||||
|
||||
public static OpenXRRestarter Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Instance == null)
|
||||
{
|
||||
var go = GameObject.Find("~oxrestarter");
|
||||
if (go == null)
|
||||
{
|
||||
go = new GameObject("~oxrestarter");
|
||||
go.hideFlags = HideFlags.HideAndDontSave;
|
||||
go.AddComponent<OpenXRRestarter>();
|
||||
}
|
||||
s_Instance = go.GetComponent<OpenXRRestarter>();
|
||||
}
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true, disables the application quitting, even when Quit is called.
|
||||
/// Used in testing, which needs to monitor for Quit to be called.
|
||||
/// </summary>
|
||||
internal static bool DisableApplicationQuit
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shutdown the the OpenXR loader and optionally quit the application
|
||||
/// </summary>
|
||||
public void Shutdown()
|
||||
{
|
||||
if (OpenXRLoader.Instance == null)
|
||||
return;
|
||||
|
||||
if (m_Coroutine != null)
|
||||
{
|
||||
Debug.LogError("Only one shutdown or restart can be executed at a time");
|
||||
return;
|
||||
}
|
||||
|
||||
m_Coroutine = StartCoroutine(RestartCoroutine(false, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restart the OpenXR loader
|
||||
/// </summary>
|
||||
public void ShutdownAndRestart()
|
||||
{
|
||||
if (OpenXRLoader.Instance == null)
|
||||
return;
|
||||
|
||||
if (m_Coroutine != null)
|
||||
{
|
||||
Debug.LogError("Only one shutdown or restart can be executed at a time");
|
||||
return;
|
||||
}
|
||||
|
||||
m_Coroutine = StartCoroutine(RestartCoroutine(true, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start a coroutine that will pause, then shut xr down, then re-initialize xr.
|
||||
/// </summary>
|
||||
public void PauseAndShutdownAndRestart()
|
||||
{
|
||||
if (OpenXRLoader.Instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine(PauseAndShutdownAndRestartCoroutine(TimeBetweenRestartAttempts));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start a coroutine that will pause, then re-initialize xr if xr hasn't already succeeded.
|
||||
/// </summary>
|
||||
public void PauseAndRetryInitialization()
|
||||
{
|
||||
if (OpenXRLoader.Instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine(PauseAndRetryInitializationCoroutine(TimeBetweenRestartAttempts));
|
||||
}
|
||||
|
||||
private void IncrementPauseAndRestartCoroutineCount()
|
||||
{
|
||||
lock (m_PauseAndRestartCoroutineCountLock)
|
||||
{
|
||||
m_pauseAndRestartCoroutineCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void DecrementPauseAndRestartCoroutineCount()
|
||||
{
|
||||
lock (m_PauseAndRestartCoroutineCountLock)
|
||||
{
|
||||
m_pauseAndRestartCoroutineCount -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator PauseAndShutdownAndRestartCoroutine(float pauseTimeInSeconds)
|
||||
{
|
||||
IncrementPauseAndRestartCoroutineCount();
|
||||
|
||||
try
|
||||
{
|
||||
// Wait a few seconds to add delay between restart requests in a restart loop.
|
||||
yield return new WaitForSeconds(pauseTimeInSeconds);
|
||||
|
||||
yield return new WaitForRestartFinish();
|
||||
|
||||
m_pauseAndRestartAttempts += 1;
|
||||
m_Coroutine = StartCoroutine(RestartCoroutine(true, true));
|
||||
}
|
||||
finally
|
||||
{
|
||||
onAfterCoroutine?.Invoke();
|
||||
}
|
||||
|
||||
DecrementPauseAndRestartCoroutineCount();
|
||||
}
|
||||
|
||||
private IEnumerator PauseAndRetryInitializationCoroutine(float pauseTimeInSeconds)
|
||||
{
|
||||
IncrementPauseAndRestartCoroutineCount();
|
||||
|
||||
try
|
||||
{
|
||||
// Wait a few seconds to add delay between restart requests in a restart loop.
|
||||
yield return new WaitForSeconds(pauseTimeInSeconds);
|
||||
|
||||
yield return new WaitForRestartFinish();
|
||||
|
||||
bool shouldSkipRestart = XRGeneralSettings.Instance.Manager.activeLoader != null;
|
||||
if (!shouldSkipRestart)
|
||||
{
|
||||
m_pauseAndRestartAttempts += 1;
|
||||
m_Coroutine = StartCoroutine(RestartCoroutine(true, false));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
onAfterCoroutine?.Invoke();
|
||||
}
|
||||
|
||||
DecrementPauseAndRestartCoroutineCount();
|
||||
}
|
||||
|
||||
private IEnumerator RestartCoroutine(bool shouldRestart, bool shouldShutdown)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (shouldShutdown)
|
||||
{
|
||||
Debug.Log("Shutting down OpenXR.");
|
||||
yield return null;
|
||||
|
||||
// Always shutdown the loader
|
||||
XRGeneralSettings.Instance.Manager.DeinitializeLoader();
|
||||
yield return null;
|
||||
|
||||
onAfterShutdown?.Invoke();
|
||||
}
|
||||
|
||||
// Restart?
|
||||
if (shouldRestart && OpenXRRuntime.ShouldRestart())
|
||||
{
|
||||
Debug.Log("Initializing OpenXR.");
|
||||
yield return XRGeneralSettings.Instance.Manager.InitializeLoader();
|
||||
|
||||
XRGeneralSettings.Instance.Manager.StartSubsystems();
|
||||
|
||||
if (XRGeneralSettings.Instance.Manager.activeLoader != null)
|
||||
{
|
||||
m_pauseAndRestartAttempts = 0;
|
||||
onAfterSuccessfulRestart?.Invoke();
|
||||
}
|
||||
|
||||
onAfterRestart?.Invoke();
|
||||
}
|
||||
// Quit?
|
||||
else if (OpenXRRuntime.ShouldQuit())
|
||||
{
|
||||
onQuit?.Invoke();
|
||||
|
||||
if (!DisableApplicationQuit)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (EditorApplication.isPlaying || EditorApplication.isPaused)
|
||||
{
|
||||
EditorApplication.ExitPlaymode();
|
||||
}
|
||||
#else
|
||||
Application.Quit();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Coroutine = null;
|
||||
onAfterCoroutine?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.xr.openxr/Runtime/OpenXRRestarter.cs.meta
Normal file
11
Packages/com.unity.xr.openxr/Runtime/OpenXRRestarter.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1e9ba50d4176d24cb202771bfcf58e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
275
Packages/com.unity.xr.openxr/Runtime/OpenXRRuntime.cs
Normal file
275
Packages/com.unity.xr.openxr/Runtime/OpenXRRuntime.cs
Normal file
@@ -0,0 +1,275 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
/// <summary>
|
||||
/// OpenXR Runtime access
|
||||
/// </summary>
|
||||
public static class OpenXRRuntime
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the current runtime.
|
||||
/// </summary>
|
||||
public static string name =>
|
||||
Internal_GetRuntimeName(out var runtimeNamePtr) ? Marshal.PtrToStringAnsi(runtimeNamePtr) : "";
|
||||
|
||||
/// <summary>
|
||||
/// Version of the current runtime.
|
||||
/// </summary>
|
||||
public static string version =>
|
||||
Internal_GetRuntimeVersion(out var major, out var minor, out var patch) ? $"{major}.{minor}.{patch}" : "";
|
||||
|
||||
/// <summary>
|
||||
/// Version of the current runtime API.
|
||||
/// </summary>
|
||||
public static string apiVersion =>
|
||||
Internal_GetAPIVersion(out var major, out var minor, out var patch) ? $"{major}.{minor}.{patch}" : "";
|
||||
|
||||
/// <summary>
|
||||
/// Version of the current runtime plug-in.
|
||||
/// </summary>
|
||||
public static string pluginVersion =>
|
||||
Internal_GetPluginVersion(out var pluginVersionPtr) ? Marshal.PtrToStringAnsi(pluginVersionPtr) : "";
|
||||
|
||||
/// <summary>
|
||||
/// Check if current runtime API version is greater than 1.1
|
||||
/// </summary>
|
||||
internal static bool isRuntimeAPIVersionGreaterThan1_1()
|
||||
{
|
||||
if (Internal_GetAPIVersion(out var major, out var minor, out var patch))
|
||||
{
|
||||
if (major >= 1 && minor >= 1)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes whether the OpenXR Extension with the given name is enabled.
|
||||
/// </summary>
|
||||
/// <param name="extensionName">Name of the extension</param>
|
||||
/// <returns>True if the extension matching the given name is enabled, false otherwise</returns>
|
||||
public static bool IsExtensionEnabled(string extensionName) => Internal_IsExtensionEnabled(extensionName);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the version number of the given extension.
|
||||
/// </summary>
|
||||
/// <param name="extensionName">Name of the extension</param>
|
||||
/// <returns>Version number of given extension, or zero if the extension was not found</returns>
|
||||
public static uint GetExtensionVersion(string extensionName) => Internal_GetExtensionVersion(extensionName);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of names of extensions enabled within the OpenXR runtime.
|
||||
/// </summary>
|
||||
/// <returns>Array of extension names or an empty array if no extensions are enabled.</returns>
|
||||
public static string[] GetEnabledExtensions()
|
||||
{
|
||||
var extensions = new string[Internal_GetEnabledExtensionCount()];
|
||||
for (var i = 0; i < extensions.Length; i++)
|
||||
{
|
||||
Internal_GetEnabledExtensionName((uint)i, out var extensionName);
|
||||
extensions[i] = $"{extensionName}";
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of names of extensions available within the OpenXR runtime.
|
||||
/// </summary>
|
||||
/// <returns>Array of extension names or an empty array if no extensions are available.</returns>
|
||||
public static string[] GetAvailableExtensions()
|
||||
{
|
||||
var extensions = new string[Internal_GetAvailableExtensionCount()];
|
||||
for (var i = 0; i < extensions.Length; i++)
|
||||
{
|
||||
Internal_GetAvailableExtensionName((uint)i, out var extensionName);
|
||||
extensions[i] = $"{extensionName}";
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This event is raised when OpenXR wants the application to quit.
|
||||
///
|
||||
/// Note that this event is raised before Application.wantsToQuit is raised and is generally
|
||||
/// raised because the Runtime has requested the application quit.
|
||||
///
|
||||
/// Return true and the quit process will continue. Return false and the quit process will cancel.
|
||||
/// </summary>
|
||||
public static event Func<bool> wantsToQuit;
|
||||
|
||||
/// <summary>
|
||||
/// This event is raised when OpenXR Runtime wants to restart the XR loader by shutting down
|
||||
/// the loader and reinitializing it.
|
||||
///
|
||||
/// Return true and the restart process will continue. Return false and the XR loader will be
|
||||
/// unloaded and the quit process will begin.
|
||||
/// </summary>
|
||||
public static event Func<bool> wantsToRestart;
|
||||
|
||||
/// <summary>
|
||||
/// This bool controls whether or not to retry initialization if the runtime reports a
|
||||
/// FORM_FACTOR_UNAVAILABLE error during initialization.
|
||||
/// </summary>
|
||||
public static bool retryInitializationOnFormFactorErrors
|
||||
{
|
||||
get
|
||||
{
|
||||
return Internal_GetSoftRestartLoopAtInitialization();
|
||||
}
|
||||
set
|
||||
{
|
||||
Internal_SetSoftRestartLoopAtInitialization(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the given event function and returns true if all invocations return true
|
||||
/// </summary>
|
||||
/// <param name="func">Event function</param>
|
||||
/// <returns>True if all event invocations return true</returns>
|
||||
private static bool InvokeEvent(Func<bool> func)
|
||||
{
|
||||
if (func == null)
|
||||
return true;
|
||||
|
||||
foreach (Func<bool> invocation in func.GetInvocationList())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!invocation())
|
||||
return false;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if UNITY_INCLUDE_TESTS
|
||||
internal static void ClearEvents()
|
||||
{
|
||||
if (wantsToQuit != null)
|
||||
foreach (Func<bool> f in wantsToQuit.GetInvocationList()) wantsToQuit -= f;
|
||||
|
||||
if (wantsToRestart != null)
|
||||
foreach (Func<bool> f in wantsToRestart.GetInvocationList()) wantsToRestart -= f;
|
||||
|
||||
wantsToQuit = null;
|
||||
wantsToRestart = null;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
internal static bool ShouldQuit() => InvokeEvent(wantsToQuit);
|
||||
internal static bool ShouldRestart() => InvokeEvent(wantsToRestart);
|
||||
|
||||
private const string LibraryName = "UnityOpenXR";
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetRuntimeName")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetRuntimeName(out IntPtr runtimeNamePtr);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetRuntimeVersion")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetRuntimeVersion(out ushort major, out ushort minor, out uint patch);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetAPIVersion")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetAPIVersion(out ushort major, out ushort minor, out uint patch);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetPluginVersion")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetPluginVersion(out IntPtr pluginVersionPtr);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "unity_ext_IsExtensionEnabled")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_IsExtensionEnabled(string extensionName);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "unity_ext_GetExtensionVersion")]
|
||||
private static extern uint Internal_GetExtensionVersion(string extensionName);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "unity_ext_GetEnabledExtensionCount")]
|
||||
private static extern uint Internal_GetEnabledExtensionCount();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "unity_ext_GetEnabledExtensionName", CharSet = CharSet.Ansi)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetEnabledExtensionNamePtr(uint index, out IntPtr outName);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "session_SetSoftRestartLoopAtInitialization")]
|
||||
private static extern void Internal_SetSoftRestartLoopAtInitialization([MarshalAs(UnmanagedType.I1)] bool value);
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "session_GetSoftRestartLoopAtInitialization")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetSoftRestartLoopAtInitialization();
|
||||
|
||||
private static bool Internal_GetEnabledExtensionName(uint index, out string extensionName)
|
||||
{
|
||||
if (!Internal_GetEnabledExtensionNamePtr(index, out var extensionNamePtr))
|
||||
{
|
||||
extensionName = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
extensionName = Marshal.PtrToStringAnsi(extensionNamePtr);
|
||||
return true;
|
||||
}
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "unity_ext_GetAvailableExtensionCount")]
|
||||
private static extern uint Internal_GetAvailableExtensionCount();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "unity_ext_GetAvailableExtensionName", CharSet = CharSet.Ansi)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetAvailableExtensionNamePtr(uint index, out IntPtr extensionName);
|
||||
|
||||
private static bool Internal_GetAvailableExtensionName(uint index, out string extensionName)
|
||||
{
|
||||
if (!Internal_GetAvailableExtensionNamePtr(index, out var extensionNamePtr))
|
||||
{
|
||||
extensionName = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
extensionName = Marshal.PtrToStringAnsi(extensionNamePtr);
|
||||
return true;
|
||||
}
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "session_GetLastError", CharSet = CharSet.Ansi)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetLastError(out IntPtr error);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the last error message that was issued in the native code
|
||||
/// </summary>
|
||||
/// <param name="error">Last error message</param>
|
||||
/// <returns>True if there was an error message, false if not</returns>
|
||||
internal static bool GetLastError(out string error)
|
||||
{
|
||||
if (!Internal_GetLastError(out var errorPtr))
|
||||
{
|
||||
error = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
error = Marshal.PtrToStringAnsi(errorPtr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs the last error message if there is one to the console
|
||||
/// </summary>
|
||||
internal static void LogLastError()
|
||||
{
|
||||
if (GetLastError(out var error))
|
||||
{
|
||||
Debug.LogError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52386df32fbf4a66af2d8a20cbeaa862
|
||||
timeCreated: 1604085922
|
||||
118
Packages/com.unity.xr.openxr/Runtime/OpenXRSettings.cs
Normal file
118
Packages/com.unity.xr.openxr/Runtime/OpenXRSettings.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
public interface IPackageSettings
|
||||
{
|
||||
OpenXRSettings GetSettingsForBuildTargetGroup(UnityEditor.BuildTargetGroup buildTargetGroup);
|
||||
string GetActiveLoaderLibraryPath();
|
||||
|
||||
/// <summary>
|
||||
/// Returns all features of a given type from all existing build target groups.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Feature type</typeparam>
|
||||
/// <returns>All known features of the given type within the package settings</returns>
|
||||
public IEnumerable<(BuildTargetGroup buildTargetGroup, T feature)> GetFeatures<T>() where T : OpenXRFeature;
|
||||
|
||||
internal void RefreshFeatureSets();
|
||||
|
||||
internal string PackageSettingsAssetPath();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Build time settings for OpenXR. These are serialized and available at runtime.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public partial class OpenXRSettings : ScriptableObject
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
internal bool versionChanged = false;
|
||||
#else
|
||||
private static OpenXRSettings s_RuntimeInstance = null;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
s_RuntimeInstance = this;
|
||||
}
|
||||
|
||||
#endif
|
||||
internal void ApplySettings()
|
||||
{
|
||||
ApplyRenderSettings();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
ApplySettings();
|
||||
}
|
||||
#endif
|
||||
|
||||
private static OpenXRSettings GetInstance(bool useActiveBuildTarget)
|
||||
{
|
||||
OpenXRSettings settings = null;
|
||||
// When running in the Unity Editor, we have to load user's customization of configuration data directly from
|
||||
// EditorBuildSettings. At runtime, we need to grab it from the static instance field instead.
|
||||
#if UNITY_EDITOR
|
||||
settings = GetSettingsForBuildTargetGroup(useActiveBuildTarget ?
|
||||
BuildPipeline.GetBuildTargetGroup(UnityEditor.EditorUserBuildSettings.activeBuildTarget) :
|
||||
BuildTargetGroup.Standalone);
|
||||
#else
|
||||
settings = s_RuntimeInstance;
|
||||
if (settings == null)
|
||||
settings = ScriptableObject.CreateInstance<OpenXRSettings>();
|
||||
#endif
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Returns the Settings object for the given BuildTargetGroup
|
||||
/// </summary>
|
||||
/// <param name="buildTargetGroup">BuildTargetGroup to request settings for</param>
|
||||
/// <returns>OpenXRSettings object for the given build target group</returns>
|
||||
public static OpenXRSettings GetSettingsForBuildTargetGroup(BuildTargetGroup buildTargetGroup)
|
||||
{
|
||||
var packageSettings = GetPackageSettings();
|
||||
if (null == packageSettings)
|
||||
return null;
|
||||
|
||||
return packageSettings.GetSettingsForBuildTargetGroup(buildTargetGroup);
|
||||
}
|
||||
|
||||
internal static IPackageSettings GetPackageSettings()
|
||||
{
|
||||
if (EditorBuildSettings.TryGetConfigObject<UnityEngine.Object>(Constants.k_SettingsKey, out var obj) && (obj is IPackageSettings packageSettings))
|
||||
return packageSettings;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Accessor to OpenXR build time settings.
|
||||
///
|
||||
/// In the Unity Editor, this returns the settings for the active build target group.
|
||||
/// </summary>
|
||||
public static OpenXRSettings ActiveBuildTargetInstance => GetInstance(true);
|
||||
|
||||
/// <summary>
|
||||
/// Accessor to OpenXR build time settings.
|
||||
///
|
||||
/// In the Unity Editor, this returns the settings for the Standalone build target group.
|
||||
/// </summary>
|
||||
public static OpenXRSettings Instance => GetInstance(false);
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.xr.openxr/Runtime/OpenXRSettings.cs.meta
Normal file
11
Packages/com.unity.xr.openxr/Runtime/OpenXRSettings.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5a1f07dc5afe854f9f12a4194aca3fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
79
Packages/com.unity.xr.openxr/Runtime/OpenXRSpaceSettings.cs
Normal file
79
Packages/com.unity.xr.openxr/Runtime/OpenXRSpaceSettings.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
public partial class OpenXRSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Activates reference space recentering when using floor-based tracking origin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// When a recentering event is performed, OpenXR will attempt to recenter the world space origin based on the local-floor reference space, if supported by the platform's hardware.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If that reference space isn't supported, OpenXR will then attempt to approximate it using stage space or local space.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Calling this method won't trigger a recenter event. This event will be sent from the platform's runtime.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="allowRecentering">Boolean value that activates the recentering feature.</param>
|
||||
/// <param name="floorOffset">Estimated height used when approximating the floor-based position when recentering the space. By default, this value is 1.5f</param>
|
||||
public static void SetAllowRecentering(bool allowRecentering, float floorOffset = 1.5f)
|
||||
{
|
||||
Internal_SetAllowRecentering(allowRecentering, floorOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates the internal XR space used for recentering, updating the tracking origin to the most recent floor-based position from the runtime.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method needs that <see cref="AllowRecentering"/> is turned on using <see cref="SetAllowRecentering"/>, otherwise it will do nothing.
|
||||
///
|
||||
/// This method doesn't trigger a recenter event, as this event has to be initiated from the platform's runtime.
|
||||
///
|
||||
/// See <see cref="SetAllowRecentering"/> for more information.
|
||||
/// </remarks>
|
||||
public static void RefreshRecenterSpace()
|
||||
{
|
||||
Internal_RegenerateTrackingOrigin();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current state of the recentering feature.
|
||||
/// </summary>
|
||||
public static bool AllowRecentering
|
||||
{
|
||||
get
|
||||
{
|
||||
return Internal_GetAllowRecentering();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current floor offset value used when approximating the floor-based position when recentering the space.
|
||||
/// </summary>
|
||||
public static float FloorOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
return Internal_GetFloorOffset();
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("UnityOpenXR", EntryPoint = "NativeConfig_SetAllowRecentering")]
|
||||
private static extern void Internal_SetAllowRecentering([MarshalAs(UnmanagedType.U1)] bool active, float height);
|
||||
|
||||
[DllImport("UnityOpenXR", EntryPoint = "NativeConfig_RegenerateTrackingOrigin")]
|
||||
private static extern void Internal_RegenerateTrackingOrigin();
|
||||
|
||||
[DllImport("UnityOpenXR", EntryPoint = "NativeConfig_GetAllowRecentering")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetAllowRecentering();
|
||||
|
||||
[DllImport("UnityOpenXR", EntryPoint = "NativeConfig_GetFloorOffsetHeight")]
|
||||
private static extern float Internal_GetFloorOffset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7ea527b59a988a44bd10ae2a1c10c3c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
306
Packages/com.unity.xr.openxr/Runtime/OpenXRUnityAPI.cs
Normal file
306
Packages/com.unity.xr.openxr/Runtime/OpenXRUnityAPI.cs
Normal file
@@ -0,0 +1,306 @@
|
||||
/// This file contains the public C# API surface for the OpenXR package.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.API
|
||||
{
|
||||
using uint32_t = UInt32;
|
||||
using UnityXRRenderTextureID = UInt32;
|
||||
|
||||
// Display scripting types are based off of IUnityXRDisplay v10 types.
|
||||
// GUID 0x7dee4aab20644831ULL, 0x92dddc65493b46bfULL
|
||||
|
||||
/// <summary>
|
||||
/// Format for color texture.
|
||||
/// </summary>
|
||||
public enum UnityXRRenderTextureFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// R8 G8 B8 A8
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFormatRGBA32,
|
||||
|
||||
/// <summary>
|
||||
/// B8 G8 R8 A8
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFormatBGRA32,
|
||||
|
||||
/// <summary>
|
||||
/// R5 G6 B5
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFormatRGB565,
|
||||
|
||||
/// <summary>
|
||||
/// R16 G16 B16 A16 signed half-float
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFormatR16G16B16A16_SFloat,
|
||||
|
||||
/// <summary>
|
||||
/// R10 G10 B10 A2 Unorm
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFormatRGBA1010102,
|
||||
|
||||
/// <summary>
|
||||
/// B10 G10 R10 A2 Unorm
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFormatBGRA1010102,
|
||||
|
||||
/// <summary>
|
||||
/// R11 G11 B10 unsigned small floating point
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFormatR11G11B10_UFloat,
|
||||
|
||||
/// <summary>
|
||||
/// Don't create a color texture, instead create a reference to another color texture that's
|
||||
/// already been created. Must fill out UnityTextureData::referenceTextureId.
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFormatReference = 64,
|
||||
|
||||
/// <summary>
|
||||
/// Don't create a whole new color texture; soft-alias the MSAA attachment, yet still
|
||||
/// construct the MSAA-resolved 1x texture as unique. This allows memory sharing for MSAA
|
||||
/// when mobile clients need to both autoresolve yet also share the MSAA textures.
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFormatSoftReferenceMSAA,
|
||||
|
||||
/// <summary>
|
||||
/// No color texture.
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFormatNone,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Container for different ways of representing texture data.
|
||||
/// - If the format (#UnityXRRenderTextureDesc::colorFormat or
|
||||
/// #UnityXRRenderTextureDesc::depthFormat) is a 'Reference' format, referenceTextureId
|
||||
/// must be set.
|
||||
/// - If the format is kUnityXRRenderTextureFormatSoftReferenceMSAA, both fields are used.
|
||||
/// Otherwise, nativePtr is used.
|
||||
/// </summary>
|
||||
public struct UnityXRTextureData
|
||||
{
|
||||
/// <summary>
|
||||
/// @brief Native texture ID if you've allocated it yourself. The texture ID varies by
|
||||
/// graphics API. For example:
|
||||
/// - GL: texture name that comes from glGenTextures
|
||||
/// - DX11: ID3D11Texture2D*
|
||||
/// - Vulkan: vkImage*
|
||||
///
|
||||
/// You can pass in #kUnityXRRenderTextureIdDontCare and have Unity allocate one for you.
|
||||
/// </summary>
|
||||
public IntPtr nativePtr;
|
||||
|
||||
/// <summary>
|
||||
/// Texture ID to share color / depth with in the case of reference color / depth format.
|
||||
/// </summary>
|
||||
public UnityXRRenderTextureID referenceTextureId;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Precision of depth texture.
|
||||
/// </summary>
|
||||
public enum UnityXRDepthTextureFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// 24-bit or greater depth texture. Unity prefers 32 bit floating point Z buffer if available on the platform.
|
||||
/// DX11: DXGI_FORMAT_D32_FLOAT_S8X24_UINT
|
||||
/// DX12: DXGI_FORMAT_D32_FLOAT_S8X24_UINT
|
||||
/// Vulkan: VK_FORMAT_D24_UNORM_S8_UINT
|
||||
/// OpenGL: Unsupported
|
||||
/// </summary>
|
||||
kUnityXRDepthTextureFormat24bitOrGreater,
|
||||
|
||||
/// <summary>
|
||||
/// If possible, use a 16-bit texture format to save bandwidth.
|
||||
/// DX11: DXGI_FORMAT_D16_UNORM
|
||||
/// DX12: DXGI_FORMAT_D16_UNORM
|
||||
/// Vulkan: VK_FORMAT_D16_UNORM
|
||||
/// OpenGL: Unsupported
|
||||
/// </summary>
|
||||
kUnityXRDepthTextureFormat16bit,
|
||||
|
||||
/// <summary>
|
||||
/// Don't create a depth texture, instead create a reference to another depth texture that's
|
||||
/// already been created. Must fill out UnityTextureData::referenceTextureId. This is
|
||||
/// useful for sharing a single depth texture between double/triple buffered color textures
|
||||
/// (of the same width/height).
|
||||
/// </summary>
|
||||
kUnityXRDepthTextureFormatReference,
|
||||
|
||||
/// <summary>
|
||||
/// No depth texture.
|
||||
/// </summary>
|
||||
kUnityXRDepthTextureFormatNone
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Format for shading rate texture.
|
||||
/// </summary>
|
||||
public enum UnityXRShadingRateFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// No shading rate texture.
|
||||
/// </summary>
|
||||
kUnityXRShadingRateFormatNone,
|
||||
|
||||
/// <summary>
|
||||
/// R8G8 shading rate texture format.
|
||||
/// </summary>
|
||||
kUnityXRShadingRateR8G8
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Flags that can be set on a UnityXRRenderTextureDesc before creation to modify behavior.
|
||||
/// </summary>
|
||||
public enum UnityXRRenderTextureFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// By default, Unity expects texture coordinates in OpenGL mapping with (0,0) in lower left
|
||||
/// hand corner. This flag specifies that (0,0) is in the upper left hand corner for this
|
||||
/// texture. Unity will flip the texture at the appropriate time.
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFlagsUVDirectionTopToBottom = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// This texture can be an unresolved MSAA texture. Accepting unresolved textures lowers
|
||||
/// the bandwidth needed by tile-based architectures.
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFlagsMultisampleAutoResolve = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that the resources backing this texture can't be resized. No control over
|
||||
/// width / height of texture. Unity might render to a separate texture of a more convenient
|
||||
/// size, then blit into this one. For Example, HoloLens backbuffer size can't be changed.
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFlagsLockedWidthHeight = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Texture can only be written to and can't be read from. Unity needs to create
|
||||
/// intermediate textures to do post-processing work.
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFlagsWriteOnly = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Use sRGB texture formats if possible.
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFlagsSRGB = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// Opt-in to always discarding depth and resolving MSAA color to improve performance on
|
||||
/// tile-based architectures at the expense of rarely-used effects which require depth
|
||||
/// resolve or MSAA color store, such as camera stacking. This only affects Vulkan. Note
|
||||
/// that this may break user content - use with care and consider giving the developer a way
|
||||
/// to turn it off.
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFlagsOptimizeBufferDiscards = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// Texture is used for storing motion-vector information.
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFlagsMotionVectorTexture = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// Texture is a "GFR texture", or more generally one which uses foveation offset
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFlagsFoveationOffset = 1 << 7,
|
||||
|
||||
/// <summary>
|
||||
/// Renderpass for this texture uses the Viewport Rect to define the Render Area
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFlagsViewportAsRenderArea = 1 << 8,
|
||||
|
||||
/// <summary>
|
||||
/// Texture is used for storing an HDR output surface
|
||||
/// </summary>
|
||||
kUnityXRRenderTextureFlagsHDR = 1 << 9,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Description of a texture that the plugin can request to be allocated via
|
||||
/// IUnityXRDisplayInterface::CreateTexture. Encapsulates both color and depth surfaces.
|
||||
/// </summary>
|
||||
public struct UnityXRRenderTextureDesc
|
||||
{
|
||||
/// <summary>
|
||||
/// Color format of the texture. Format is sRGB if kUnityXRRenderTextureFlagsSRGB is set
|
||||
/// and there is an equivalent sRGB native format.
|
||||
/// </summary>
|
||||
public UnityXRRenderTextureFormat colorFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Data for color texture.
|
||||
/// </summary>
|
||||
public UnityXRTextureData color;
|
||||
|
||||
/// <summary>
|
||||
/// Depth format of the texture.
|
||||
/// </summary>
|
||||
public UnityXRDepthTextureFormat depthFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Data for depth texture.
|
||||
/// </summary>
|
||||
public UnityXRTextureData depth;
|
||||
|
||||
/// <summary>
|
||||
/// Shading rate texture format.
|
||||
/// </summary>
|
||||
public UnityXRShadingRateFormat shadingRateFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Data for shading rate texture.
|
||||
/// </summary>
|
||||
public UnityXRTextureData shadingRate;
|
||||
|
||||
/// <summary>
|
||||
/// Width of the texture in pixels.
|
||||
/// </summary>
|
||||
public uint32_t width;
|
||||
|
||||
/// <summary>
|
||||
/// Height of the texture in pixels.
|
||||
/// </summary>
|
||||
public uint32_t height;
|
||||
|
||||
/// <summary>
|
||||
/// If requesting a texture array, the length of the texture array.
|
||||
/// </summary>
|
||||
public uint32_t textureArrayLength;
|
||||
|
||||
/// <summary>
|
||||
/// Combination of #UnityXRRenderTextureFlags.
|
||||
/// </summary>
|
||||
public uint32_t flags;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// static container for XRDisplay-related scripting functionality.
|
||||
/// </summary>
|
||||
public static class UnityXRDisplay
|
||||
{
|
||||
/// <summary>
|
||||
/// Unity will allocate the texture if needed. #kUnityXRRenderTextureIdDontCare can be set
|
||||
/// on UnityXRRenderTextureDesc.nativeColorTexPtr or
|
||||
/// UnityXRRenderTextureDesc.nativeDepthTexPtr.
|
||||
/// </summary>
|
||||
public const UnityXRRenderTextureID kUnityXRRenderTextureIdDontCare = 0;
|
||||
|
||||
private const string k_UnityOpenXRLib = "UnityOpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// Create a UnityXRRenderTextureId given a UnityXRRenderTextureDesc.
|
||||
/// </summary>
|
||||
/// <param name="desc">Descriptor of the texture to be created.</param>
|
||||
/// <param name="id">Returned Texture ID representing a unique instance of a texture.</param>
|
||||
/// <returns>
|
||||
/// true Successfully initialized
|
||||
/// false Error
|
||||
/// </returns>
|
||||
[DllImport(k_UnityOpenXRLib, EntryPoint = "Display_CreateTexture")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool CreateTexture(UnityXRRenderTextureDesc desc, out uint32_t id);
|
||||
}
|
||||
} // namespace
|
||||
11
Packages/com.unity.xr.openxr/Runtime/OpenXRUnityAPI.cs.meta
Normal file
11
Packages/com.unity.xr.openxr/Runtime/OpenXRUnityAPI.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea7297cd48d271546a6341cf47ec04e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
64
Packages/com.unity.xr.openxr/Runtime/OpenXRUtility.cs
Normal file
64
Packages/com.unity.xr.openxr/Runtime/OpenXRUtility.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR
|
||||
{
|
||||
/// <summary>
|
||||
/// OpenXR Utility Class contains helper methods that any script can use.
|
||||
/// </summary>
|
||||
public static class OpenXRUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the inverse of the given pose.
|
||||
/// </summary>
|
||||
private static Pose Inverse(Pose p)
|
||||
{
|
||||
Pose ret;
|
||||
ret.rotation = Quaternion.Inverse(p.rotation);
|
||||
ret.position = ret.rotation * -p.position;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculate object position and rotation from tracking-space to world-space, for use cases like teleporting.
|
||||
/// </summary>
|
||||
/// <param name="t">original transform of the object in the scene, typically obtained by gameObject.transform</param>
|
||||
/// <param name="camera">camera the calculation is based on, normally it is the main camera</param>
|
||||
/// <returns>the recalculated pose <see cref="UnityEngine.Pose"/> in world-space.</returns>
|
||||
public static Pose ComputePoseToWorldSpace(Transform t, Camera camera)
|
||||
{
|
||||
if (camera == null)
|
||||
return default;
|
||||
|
||||
Transform cameraTransform = camera.transform;
|
||||
Pose headPose = new Pose(cameraTransform.localPosition, cameraTransform.localRotation);
|
||||
Pose camPose = new Pose(cameraTransform.position, cameraTransform.rotation);
|
||||
Pose transformPose = new Pose(t.position, t.rotation);
|
||||
|
||||
Pose headSpacePose = transformPose.GetTransformedBy(Inverse(camPose));
|
||||
return headSpacePose.GetTransformedBy(headPose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the current session is in the focused state.
|
||||
/// See <a href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#session-states">XR_SESSION_STATE_FOCUSED.</a> for reference.
|
||||
/// </summary>
|
||||
public static bool IsSessionFocused => Internal_IsSessionFocused();
|
||||
/// <summary>
|
||||
/// Returns the change of user presence, such as when the user has taken off or put on an XR headset.
|
||||
/// If the system does not support user presence sensing, runtime assumes that the user is always present and IsUserPresent always returns True.
|
||||
/// If the system supports the sensing of user presence, returns true when detected the presence of a user and returns false when detected the absence of a user.
|
||||
/// See <a href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_user_presence">XR_EXT_user_presence.</a> for reference.
|
||||
/// </summary>
|
||||
public static bool IsUserPresent => Internal_GetUserPresence();
|
||||
|
||||
private const string LibraryName = "UnityOpenXR";
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_IsSessionFocused")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_IsSessionFocused();
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetUserPresence")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern bool Internal_GetUserPresence();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 841a900f196540ef9263a286f9f36890
|
||||
timeCreated: 1710277080
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user