123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- // ENABLE_VR is not defined on Game Core but the assembly is available with limited features when the XR module is enabled.
- #if UNITY_INPUT_SYSTEM_ENABLE_XR && (ENABLE_VR || UNITY_GAMECORE) && !UNITY_FORCE_INPUTSYSTEM_XR_OFF
- using System;
- using System.Collections.Generic;
- using UnityEngine.InputSystem.LowLevel;
- using UnityEngine.InputSystem.Utilities;
- using System.Text;
- using UnityEngine.InputSystem.Layouts;
- using UnityEngine.XR;
-
- namespace UnityEngine.InputSystem.XR
- {
- internal class XRLayoutBuilder
- {
- private string parentLayout;
- private string interfaceName;
- private XRDeviceDescriptor descriptor;
-
- private static uint GetSizeOfFeature(XRFeatureDescriptor featureDescriptor)
- {
- switch (featureDescriptor.featureType)
- {
- case FeatureType.Binary:
- return sizeof(byte);
- case FeatureType.DiscreteStates:
- return sizeof(int);
- case FeatureType.Axis1D:
- return sizeof(float);
- case FeatureType.Axis2D:
- return sizeof(float) * 2;
- case FeatureType.Axis3D:
- return sizeof(float) * 3;
- case FeatureType.Rotation:
- return sizeof(float) * 4;
- case FeatureType.Hand:
- return sizeof(uint) * 26;
- case FeatureType.Bone:
- return sizeof(uint) + (sizeof(float) * 3) + (sizeof(float) * 4);
- case FeatureType.Eyes:
- return (sizeof(float) * 3) * 3 + ((sizeof(float) * 4) * 2) + (sizeof(float) * 2);
- case FeatureType.Custom:
- return featureDescriptor.customSize;
- }
- return 0;
- }
-
- private static string SanitizeString(string original, bool allowPaths = false)
- {
- var stringLength = original.Length;
- var sanitizedName = new StringBuilder(stringLength);
- for (var i = 0; i < stringLength; i++)
- {
- var letter = original[i];
- if (char.IsUpper(letter) || char.IsLower(letter) || char.IsDigit(letter) || letter == '_' || (allowPaths && (letter == '/')))
- {
- sanitizedName.Append(letter);
- }
- }
- return sanitizedName.ToString();
- }
-
- internal static string OnFindLayoutForDevice(ref InputDeviceDescription description, string matchedLayout,
- InputDeviceExecuteCommandDelegate executeCommandDelegate)
- {
- // If the device isn't a XRInput, we're not interested.
- if (description.interfaceName != XRUtilities.InterfaceCurrent && description.interfaceName != XRUtilities.InterfaceV1)
- {
- return null;
- }
-
- // If the description doesn't come with a XR SDK descriptor, we're not
- // interested either.
- if (string.IsNullOrEmpty(description.capabilities))
- {
- return null;
- }
-
- // Try to parse the XR descriptor.
- XRDeviceDescriptor deviceDescriptor;
- try
- {
- deviceDescriptor = XRDeviceDescriptor.FromJson(description.capabilities);
- }
- catch (Exception)
- {
- return null;
- }
-
- if (deviceDescriptor == null)
- {
- return null;
- }
-
- if (string.IsNullOrEmpty(matchedLayout))
- {
- const InputDeviceCharacteristics controllerCharacteristics = InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.Controller;
- if ((deviceDescriptor.characteristics & InputDeviceCharacteristics.HeadMounted) != 0)
- matchedLayout = "XRHMD";
- else if ((deviceDescriptor.characteristics & controllerCharacteristics) == controllerCharacteristics)
- matchedLayout = "XRController";
- }
-
- string layoutName;
- if (string.IsNullOrEmpty(description.manufacturer))
- {
- layoutName = $"{SanitizeString(description.interfaceName)}::{SanitizeString(description.product)}";
- }
- else
- {
- layoutName =
- $"{SanitizeString(description.interfaceName)}::{SanitizeString(description.manufacturer)}::{SanitizeString(description.product)}";
- }
-
- var layout = new XRLayoutBuilder { descriptor = deviceDescriptor, parentLayout = matchedLayout, interfaceName = description.interfaceName };
- InputSystem.RegisterLayoutBuilder(() => layout.Build(), layoutName, matchedLayout);
-
- return layoutName;
- }
-
- private static string ConvertPotentialAliasToName(InputControlLayout layout, string nameOrAlias)
- {
- var internedNameOrAlias = new InternedString(nameOrAlias);
- var controls = layout.controls;
- for (var i = 0; i < controls.Count; i++)
- {
- var controlItem = controls[i];
-
- if (controlItem.name == internedNameOrAlias)
- return nameOrAlias;
-
- var aliases = controlItem.aliases;
- for (var j = 0; j < aliases.Count; j++)
- {
- if (aliases[j] == nameOrAlias)
- return controlItem.name.ToString();
- }
- }
- return nameOrAlias;
- }
-
- private bool IsSubControl(string name)
- {
- return name.Contains('/');
- }
-
- private string GetParentControlName(string name)
- {
- int idx = name.IndexOf('/');
- return name.Substring(0, idx);
- }
-
- static readonly string[] poseSubControlNames =
- {
- "/isTracked",
- "/trackingState",
- "/position",
- "/rotation",
- "/velocity",
- "/angularVelocity"
- };
-
- static readonly FeatureType[] poseSubControlTypes =
- {
- FeatureType.Binary,
- FeatureType.DiscreteStates,
- FeatureType.Axis3D,
- FeatureType.Rotation,
- FeatureType.Axis3D,
- FeatureType.Axis3D
- };
-
- // A PoseControl consists of 6 subcontrols with specific names and types
- private bool IsPoseControl(List<XRFeatureDescriptor> features, int startIndex)
- {
- for (var i = 0; i < 6; i++)
- {
- if (!features[startIndex + i].name.EndsWith(poseSubControlNames[i]) ||
- features[startIndex + i].featureType != poseSubControlTypes[i])
- return false;
- }
- return true;
- }
-
- private InputControlLayout Build()
- {
- var builder = new InputControlLayout.Builder
- {
- stateFormat = new FourCC('X', 'R', 'S', '0'),
- extendsLayout = parentLayout,
- updateBeforeRender = true
- };
-
- var inheritedLayout = !string.IsNullOrEmpty(parentLayout)
- ? InputSystem.LoadLayout(parentLayout)
- : null;
-
- var parentControls = new List<string>();
- var currentUsages = new List<string>();
-
- uint currentOffset = 0;
- for (var i = 0; i < descriptor.inputFeatures.Count; i++)
- {
- var feature = descriptor.inputFeatures[i];
- currentUsages.Clear();
-
- if (feature.usageHints != null)
- {
- foreach (var usageHint in feature.usageHints)
- {
- if (!string.IsNullOrEmpty(usageHint.content))
- currentUsages.Add(usageHint.content);
- }
- }
-
- var featureName = feature.name;
- featureName = SanitizeString(featureName, true);
- if (inheritedLayout != null)
- featureName = ConvertPotentialAliasToName(inheritedLayout, featureName);
-
- featureName = featureName.ToLower();
-
- if (IsSubControl(featureName))
- {
- string parentControl = GetParentControlName(featureName);
- if (!parentControls.Contains(parentControl))
- {
- if (IsPoseControl(descriptor.inputFeatures, i))
- {
- builder.AddControl(parentControl)
- .WithLayout("Pose")
- .WithByteOffset(0);
- parentControls.Add(parentControl);
- }
- }
- }
-
- uint nextOffset = GetSizeOfFeature(feature);
- if (interfaceName == XRUtilities.InterfaceV1)
- {
- #if UNITY_ANDROID
- if (nextOffset < 4)
- nextOffset = 4;
- #endif
- }
- else
- {
- if (nextOffset >= 4 && (currentOffset % 4 != 0))
- currentOffset += (4 - (currentOffset % 4));
- }
-
-
- switch (feature.featureType)
- {
- case FeatureType.Binary:
- {
- builder.AddControl(featureName)
- .WithLayout("Button")
- .WithByteOffset(currentOffset)
- .WithFormat(InputStateBlock.FormatBit)
- .WithUsages(currentUsages);
- break;
- }
- case FeatureType.DiscreteStates:
- {
- builder.AddControl(featureName)
- .WithLayout("Integer")
- .WithByteOffset(currentOffset)
- .WithFormat(InputStateBlock.FormatInt)
- .WithUsages(currentUsages);
- break;
- }
- case FeatureType.Axis1D:
- {
- builder.AddControl(featureName)
- .WithLayout("Analog")
- .WithRange(-1, 1)
- .WithByteOffset(currentOffset)
- .WithFormat(InputStateBlock.FormatFloat)
- .WithUsages(currentUsages);
- break;
- }
- case FeatureType.Axis2D:
- {
- builder.AddControl(featureName)
- .WithLayout("Stick")
- .WithByteOffset(currentOffset)
- .WithFormat(InputStateBlock.FormatVector2)
- .WithUsages(currentUsages);
-
- builder.AddControl(featureName + "/x")
- .WithLayout("Analog")
- .WithRange(-1, 1);
- builder.AddControl(featureName + "/y")
- .WithLayout("Analog")
- .WithRange(-1, 1);
- break;
- }
- case FeatureType.Axis3D:
- {
- builder.AddControl(featureName)
- .WithLayout("Vector3")
- .WithByteOffset(currentOffset)
- .WithFormat(InputStateBlock.FormatVector3)
- .WithUsages(currentUsages);
- break;
- }
- case FeatureType.Rotation:
- {
- builder.AddControl(featureName)
- .WithLayout("Quaternion")
- .WithByteOffset(currentOffset)
- .WithFormat(InputStateBlock.FormatQuaternion)
- .WithUsages(currentUsages);
- break;
- }
- case FeatureType.Hand:
- {
- break;
- }
- case FeatureType.Bone:
- {
- builder.AddControl(featureName)
- .WithLayout("Bone")
- .WithByteOffset(currentOffset)
- .WithUsages(currentUsages);
- break;
- }
- case FeatureType.Eyes:
- {
- builder.AddControl(featureName)
- .WithLayout("Eyes")
- .WithByteOffset(currentOffset)
- .WithUsages(currentUsages);
- break;
- }
- }
- currentOffset += nextOffset;
- }
-
- return builder.Build();
- }
- }
- }
- #endif
|