test
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
// 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
|
Reference in New Issue
Block a user