// "VIVE SDK // © 2020 HTC Corporation. All Rights Reserved. // // Unless otherwise required by copyright law and practice, // upon the execution of HTC SDK license agreement, // HTC grants you access to and use of the VIVE SDK(s). // You shall fully comply with all of HTC’s SDK license agreement terms and // conditions signed by you and all SDK and API requirements, // specifications, and documentation provided by HTC to You." using UnityEngine.Scripting; using UnityEngine.XR.OpenXR.Features; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.XR; using UnityEngine.InputSystem.Controls; using UnityEngine.XR.OpenXR; using UnityEngine; using UnityEngine.InputSystem; using System.Collections.Generic; using UnityEngine.XR; using UnityEngine.XR.OpenXR.Input; #if UNITY_EDITOR using UnityEditor; using UnityEditor.XR.OpenXR.Features; #endif #if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0. using PoseControl = UnityEngine.InputSystem.XR.PoseControl; #else using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl; #endif namespace VIVE.OpenXR.Tracker { /// /// This enables the use of wrist tracker interaction profiles in OpenXR. It enables XR_HTC_vive_wrist_tracker_interaction in the underyling runtime. /// This creates a new with the characteristic. /// #if UNITY_EDITOR [OpenXRFeature(UiName = "VIVE XR Wrist Tracker", BuildTargetGroups = new[] { BuildTargetGroup.Android , BuildTargetGroup.Standalone}, Company = "HTC", Desc = "Support for enabling the wrist tracker interaction profile. Will register the controller map for wrist tracker if enabled.", DocumentationLink = "..\\Documentation", Version = "1.0.0", OpenxrExtensionStrings = kOpenxrExtensionString, Category = FeatureCategory.Interaction, FeatureId = featureId)] #endif public class ViveWristTracker : OpenXRInteractionFeature { const string LOG_TAG = "VIVE.OpenXR.Tracker.ViveWristTracker"; void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); } void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); } /// /// OpenXR specification 12.72. XR_HTC_vive_wrist_tracker_interaction. /// public const string kOpenxrExtensionString = "XR_HTC_vive_wrist_tracker_interaction"; /// /// The feature id string. This is used to give the feature a well known id for reference. /// public const string featureId = "vive.openxr.feature.tracker"; /// The interaction profile string used to reference the wrist tracker interaction input device. private const string profile = "/interaction_profiles/htc/vive_wrist_tracker"; private const string leftWrist = "/user/wrist_htc/left"; private const string rightWrist = "/user/wrist_htc/right"; // Available Bindings // Left Hand Only /// /// 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 user path. /// public const string buttonX = "/input/x/click"; /// /// 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 user path. /// public const string menu = "/input/menu/click"; // Right Hand Only /// /// 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 user path. /// public const string buttonA = "/input/a/click"; /// /// 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 user path. /// public const string system = "/input/system/click"; // Both Hands /// /// Constant for a pose interaction binding '.../input/entity_htc/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string entityPose = "/input/entity_htc/pose"; [Preserve, InputControlLayout(displayName = "Vive Wrist Tracker (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" }, isGenericTypeOfDevice = true)] public class WristTrackerDevice : OpenXRDevice { const string LOG_TAG = "VIVE.OpenXR.Tracker.ViveWristTracker.WristTrackerDevice"; void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); } /// /// A ButtonControl that represents the OpenXR bindings, depending on handedness. /// [Preserve, InputControl(aliases = new[] { "A", "X", "buttonA", "buttonX" }, usage = "PrimaryButton")] public ButtonControl primaryButton { get; private set; } /// /// A that represents the OpenXR binding. The entity pose represents the location of the tracker. /// [Preserve, InputControl(offset = 0, aliases = new[] { "device", "entityPose" }, usage = "Device")] public PoseControl devicePose { get; private set; } /// /// A 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. /// [Preserve, InputControl(offset = 24, usage = "IsTracked")] public ButtonControl isTracked { get; private set; } /// /// A 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. /// [Preserve, InputControl(offset = 28, usage = "TrackingState")] public IntegerControl trackingState { get; private set; } /// /// A Vector3Control required for backwards compatibility with the XRSDK layouts. This is the device position. For the VIVE XR device, this is both the device and the pointer position. This value is equivalent to mapping devicePose/position. /// [Preserve, InputControl(offset = 32, alias = "gripPosition")] public Vector3Control devicePosition { get; private set; } /// /// A QuaternionControl required for backwards compatibility with the XRSDK layouts. This is the device orientation. For the VIVE XR device, this is both the device and the pointer rotation. This value is equivalent to mapping devicePose/rotation. /// [Preserve, InputControl(offset = 44, alias = "gripOrientation")] public QuaternionControl deviceRotation { get; private set; } /// /// A ButtonControl that represents the OpenXR bindings, depending on handedness. /// [Preserve, InputControl(aliases = new[] { "menuButton" }, usage = "MenuButton")] public ButtonControl menu { get; private set; } /// /// Internal call used to assign controls to the the correct element. /// protected override void FinishSetup() { /*for (int i = 0; i < InputSystem.devices.Count; i++) { var description = InputSystem.devices[i].description; DEBUG("FinishSetup() device[" + i + "], interfaceName: " + description.interfaceName + ", deviceClass: " + description.deviceClass + ", product: " + description.product); }*/ base.FinishSetup(); primaryButton = GetChildControl("primaryButton"); menu = GetChildControl("menu"); devicePose = GetChildControl("devicePose"); isTracked = GetChildControl("isTracked"); trackingState = GetChildControl("trackingState"); devicePosition = GetChildControl("devicePosition"); deviceRotation = GetChildControl("deviceRotation"); } } #pragma warning disable private bool m_XrInstanceCreated = false; #pragma warning restore private XrInstance m_XrInstance = 0; /// /// Called when xrCreateInstance is done. /// /// The created instance. /// True for valid XrInstance protected override bool OnInstanceCreate(ulong xrInstance) { // Requires the eye tracking extension /*if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString)) { WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled."); return false; }*/ m_XrInstanceCreated = true; m_XrInstance = xrInstance; DEBUG("OnInstanceCreate() " + m_XrInstance); return base.OnInstanceCreate(xrInstance); } private const string kLayoutName = "ViveWristTracker"; private const string kDeviceLocalizedName = "Vive Wrist Tracker OpenXR"; /// /// Registers the layout with the Input System. /// protected override void RegisterDeviceLayout() { InputSystem.RegisterLayout(typeof(WristTrackerDevice), kLayoutName, matches: new InputDeviceMatcher() .WithInterface(XRUtilities.InterfaceMatchAnyVersion) .WithProduct(kDeviceLocalizedName)); } /// /// Removes the layout from the Input System. /// protected override void UnregisterDeviceLayout() { InputSystem.RemoveLayout(kLayoutName); } /// /// Registers action maps to Unity XR. /// protected override void RegisterActionMapsWithRuntime() { ActionMapConfig actionMap = new ActionMapConfig() { name = "vivewristtracker", localizedName = kDeviceLocalizedName, desiredInteractionProfile = profile, manufacturer = "HTC", serialNumber = "", deviceInfos = new List() { new DeviceConfig() { characteristics = InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Left, userPath = leftWrist // "/user/wrist_htc/left" }, new DeviceConfig() { characteristics = InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Right, userPath = rightWrist // "/user/wrist_htc/right" } }, actions = new List() { // X / A Press new ActionConfig() { name = "primaryButton", localizedName = "Primary Pressed", type = ActionType.Binary, usages = new List() { "PrimaryButton" }, bindings = new List() { new ActionBinding() { interactionPath = buttonX, interactionProfileName = profile, userPaths = new List() { leftWrist } }, new ActionBinding() { interactionPath = buttonA, interactionProfileName = profile, userPaths = new List() { rightWrist } }, } }, // Menu new ActionConfig() { name = "menu", localizedName = "Menu", type = ActionType.Binary, usages = new List() { "MenuButton" }, bindings = new List() { new ActionBinding() { interactionPath = menu, interactionProfileName = profile, userPaths = new List() { leftWrist } }, new ActionBinding() { interactionPath = system, interactionProfileName = profile, userPaths = new List() { rightWrist } }, } }, // Device Pose new ActionConfig() { name = "devicePose", localizedName = "Grip Pose", type = ActionType.Pose, usages = new List() { "Device" }, bindings = new List() { new ActionBinding() { interactionPath = entityPose, interactionProfileName = profile, } } } } }; AddActionMap(actionMap); } } }