// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
#if ULTIMATEXR_USE_STEAMVR_SDK
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UltimateXR.Core;
using UltimateXR.Core.Settings;
using UltimateXR.Devices;
using UltimateXR.Devices.Integrations.SteamVR;
using UltimateXR.Extensions.System.IO;
using UnityEditor;
using UnityEngine;
using Valve.Newtonsoft.Json;
using Valve.VR;
#endif
namespace UltimateXR.Editor.Sdks.InputTracking
{
#if ULTIMATEXR_USE_STEAMVR_SDK
///
/// Class with functionality to create and remove the required SteamVR actions to interface with UltimateXR.
///
public static partial class SteamVRActionsExporter
{
#region Public Methods
///
/// If SteamVR has the actions already set up, checks if UltimateXR actions need to be setup.
///
/// True if SteamVR actions are present but UltimateXR custom actions are missing
public static bool NeedsActionsSetup()
{
if (SteamVR_Input.DoesActionsFileExist() && SteamVR_Input.actionFile != null && SteamVR_Input.actionFile.action_sets != null)
{
// Action set registered?
SteamVR_Input_ActionFile_ActionSet actionSet = SteamVR_Input.actionFile.action_sets.FirstOrDefault(a => a.name == ActionSetName);
if (actionSet == null)
{
return true;
}
if (!string.Equals(actionSet.usage, SteamVR_Input_ActionFile_ActionSet_Usages.single))
{
return true;
}
// All actions registered?
foreach (SteamVR_Input_ActionFile_Action action in EnumerateCustomActionsToBeAdded(actionSet))
{
if (!IsActionPresent(actionSet, action))
{
return true;
}
}
return false;
}
return false;
}
///
/// Tries to set up UltimateXR SteamVR actions to interface with the input system.
///
public static void TrySetupActions()
{
if (SteamVR_Input.DoesActionsFileExist())
{
// Action file not loaded?
if (SteamVR_Input.actionFile == null)
{
SteamVR_Input.InitializeFile(false, false);
}
// Only process if SteamVR actions are available
if (SteamVR_Input.actionFile != null && SteamVR_Input.actionFile.action_sets != null)
{
// Try to find our action set
SteamVR_Input_ActionFile_ActionSet actionSet = SteamVR_Input.actionFile.action_sets.FirstOrDefault(a => a.name == ActionSetName);
// If not present, create:
if (actionSet == null)
{
actionSet = new SteamVR_Input_ActionFile_ActionSet
{
name = ActionSetName,
usage = SteamVR_Input_ActionFile_ActionSet_Usages.single
};
SteamVR_Input.actionFile.action_sets.Add(actionSet);
}
else
{
actionSet.usage = SteamVR_Input_ActionFile_ActionSet_Usages.single;
}
// Clear list and add actions. This will remove deprecated actions in the future.
actionSet.actionsInList = new List();
actionSet.actionsOutList = new List();
foreach (SteamVR_Input_ActionFile_Action action in EnumerateCustomActionsToBeAdded(actionSet))
{
if (!IsActionPresent(actionSet, action))
{
if (action.direction == SteamVR_ActionDirections.In)
{
actionSet.actionsInList.Add(action);
}
else
{
actionSet.actionsOutList.Add(action);
}
}
else
{
if (UxrGlobalSettings.Instance.LogLevelDevices >= UxrLogLevel.Warnings)
{
Debug.LogWarning($"{UxrConstants.DevicesModule}: Warning. Action {action.name} already exists. Skipping...");
}
}
}
// Generate bindings
TryGenerateBindings();
// Save at the end
EditorApplication.delayCall += SaveSteamVRActions;
}
}
}
///
/// Tries to remove SteamVR actions to interface with the UltimateXR input system.
///
public static void TryRemoveActions()
{
if (SteamVR_Input.DoesActionsFileExist())
{
// Action file not loaded?
if (SteamVR_Input.actionFile == null)
{
SteamVR_Input.InitializeFile(false, false);
}
// Only process if SteamVR actions are available
if (SteamVR_Input.actionFile != null && SteamVR_Input.actionFile.action_sets != null)
{
// Try to find action set and remove it.
// TODO: SteamVR has a bug where the action set is not removed but at least the actions are
int index = SteamVR_Input.actionFile.action_sets.FindIndex(a => a.name == ActionSetName);
if (index != -1)
{
SteamVR_Input.actionFile.action_sets.RemoveAt(index);
EditorApplication.delayCall += SaveSteamVRActions;
}
}
}
}
#endregion
#region Private Methods
///
/// Saves the SteamVR actions to disk.
///
private static void SaveSteamVRActions()
{
SteamVR_Input.actionFile.SaveHelperLists();
SteamVR_Input.actionFile.Save(SteamVR_Input.GetActionsFilePath());
SteamVR_Input_ActionManifest_Manager.CleanBindings(true);
SteamVR_Input_Generator.BeginGeneration();
}
///
/// Checks if an action is present in a SteamVR action set.
///
/// Action set to process
/// Action to look for
/// True if the action was found
private static bool IsActionPresent(SteamVR_Input_ActionFile_ActionSet actionSet, SteamVR_Input_ActionFile_Action action)
{
if (action.direction == SteamVR_ActionDirections.In)
{
SteamVR_Input_ActionFile_Action existingAction = actionSet.actionsInList.FirstOrDefault(a => string.Equals(a.name, action.name));
return existingAction != null && existingAction.Equals(action);
}
else
{
SteamVR_Input_ActionFile_Action existingAction = actionSet.actionsOutList.FirstOrDefault(a => string.Equals(a.name, action.name));
return existingAction != null && existingAction.Equals(action);
}
}
///
/// Enumerates all the SteamVR actions that need to be added in order to translate SteamVR input to UltimateXR input.
///
/// Action set that the actions will belong to
/// Enumerable collection with required SteamVR actions
private static IEnumerable EnumerateCustomActionsToBeAdded(SteamVR_Input_ActionFile_ActionSet actionSet)
{
// Buttons
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Joystick);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Joystick2);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Trigger);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Trigger2);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Grip);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Button1);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Button2);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Button3);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Button4);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Bumper);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Bumper2);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Back);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.Menu);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.ThumbCapSense);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.IndexCapSense);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.MiddleCapSense);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.RingCapSense);
yield return GetNewActionButtonClick(actionSet, UxrInputButtons.LittleCapSense);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Joystick);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Joystick2);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Trigger);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Trigger2);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Grip);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Button1);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Button2);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Button3);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Button4);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Bumper);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Bumper2);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Back);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.Menu);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.ThumbCapSense);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.IndexCapSense);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.MiddleCapSense);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.RingCapSense);
yield return GetNewActionButtonTouch(actionSet, UxrInputButtons.LittleCapSense);
// UxrInput1D
yield return GetNewAction(actionSet, UxrInput1D.Grip.ToString(), SteamVR_ActionDirections.In, UxrSteamVRConstants.BindingVarVector1);
yield return GetNewAction(actionSet, UxrInput1D.Trigger.ToString(), SteamVR_ActionDirections.In, UxrSteamVRConstants.BindingVarVector1);
yield return GetNewAction(actionSet, UxrInput1D.Trigger2.ToString(), SteamVR_ActionDirections.In, UxrSteamVRConstants.BindingVarVector1);
// UxrInput2D
yield return GetNewAction(actionSet, UxrInput2D.Joystick.ToString(), SteamVR_ActionDirections.In, UxrSteamVRConstants.BindingVarVector2);
yield return GetNewAction(actionSet, UxrInput2D.Joystick2.ToString(), SteamVR_ActionDirections.In, UxrSteamVRConstants.BindingVarVector2);
// Hand skeletons
yield return GetNewActionSkeleton(actionSet, UxrSteamVRConstants.ActionNameHandSkeletonLeft, true);
yield return GetNewActionSkeleton(actionSet, UxrSteamVRConstants.ActionNameHandSkeletonRight, false);
// Haptic feedback
yield return GetNewActionHaptic(actionSet, UxrSteamVRConstants.ActionNameHandHaptics);
}
///
/// Creates a new SteamVR action object.
///
/// Action set this action will belong to
/// Action name
/// Input or output action?
/// Variable type associated to the action
/// New SteamVR action object
private static SteamVR_Input_ActionFile_Action GetNewAction(SteamVR_Input_ActionFile_ActionSet actionSet, string actionName, SteamVR_ActionDirections direction, string varType)
{
SteamVR_Input_ActionFile_Action action = new SteamVR_Input_ActionFile_Action
{
name = SteamVR_Input_ActionFile_Action.CreateNewName(actionSet.shortName, direction, actionName.ToLower()) + "_" + varType,
type = varType,
requirement = SteamVR_Input_ActionFile_Action_Requirements.optional.ToString()
};
return action;
}
///
/// Simplified method to create a new SteamVR action object for a button click.
///
/// Action set this action will belong to
/// Button to generate action for
/// Action object representing the action of a button click
private static SteamVR_Input_ActionFile_Action GetNewActionButtonClick(SteamVR_Input_ActionFile_ActionSet actionSet, UxrInputButtons button)
{
return GetNewAction(actionSet, $"{button}_{UxrSteamVRConstants.BindingInputClick}", SteamVR_ActionDirections.In, UxrSteamVRConstants.BindingVarBool);
}
///
/// Simplified method to create a new SteamVR action object for a button touch.
///
/// Action set this action will belong to
/// Button to generate action for
/// Action object representing the action of a button touch
private static SteamVR_Input_ActionFile_Action GetNewActionButtonTouch(SteamVR_Input_ActionFile_ActionSet actionSet, UxrInputButtons button)
{
return GetNewAction(actionSet, $"{button}_{UxrSteamVRConstants.BindingInputTouch}", SteamVR_ActionDirections.In, UxrSteamVRConstants.BindingVarBool);
}
///
/// Simplified method to create a new SteamVR action object for skeleton tracking.
///
/// Action set this action will belong to
/// Name of the action
/// Is it for the left hand or right hand?
/// Action object representing the action of hand skeleton tracking
private static SteamVR_Input_ActionFile_Action GetNewActionSkeleton(SteamVR_Input_ActionFile_ActionSet actionSet, string actionName, bool isLeft)
{
SteamVR_Input_ActionFile_Action action = new SteamVR_Input_ActionFile_Action
{
name = SteamVR_Input_ActionFile_Action.CreateNewName(actionSet.shortName, SteamVR_ActionDirections.In, actionName),
type = SteamVR_Input_ActionFile_ActionTypes.skeleton,
skeleton = SteamVR_Input_ActionFile_ActionTypes.listSkeletons[isLeft ? 0 : 1].Replace("\\", "/"),
requirement = SteamVR_Input_ActionFile_Action_Requirements.optional.ToString()
};
return action;
}
///
/// Simplified method to create a new SteamVR action object for haptic feedback.
///
/// Action set this action will belong to
/// Name of the action
/// Action object representing the action
private static SteamVR_Input_ActionFile_Action GetNewActionHaptic(SteamVR_Input_ActionFile_ActionSet actionSet, string actionName)
{
SteamVR_Input_ActionFile_Action action = new SteamVR_Input_ActionFile_Action
{
name = SteamVR_Input_ActionFile_Action.CreateNewName(actionSet.shortName, SteamVR_ActionDirections.Out, actionName),
type = SteamVR_Input_ActionFile_ActionTypes.vibration
};
return action;
}
///
/// Tries to generate bindings for all the registered SteamVR binding files.
/// Files are deserialized, processed and if necessary changes are saved back to disk.
///
private static void TryGenerateBindings()
{
// Iterate over binding files
foreach (SteamVR_Input_ActionFile_DefaultBinding binding in SteamVR_Input.actionFile.default_bindings)
{
// Compose full path
string bindingsFilePath = PathExt.Combine(SteamVR_Input.GetActionsFileFolder(), binding.binding_url);
if (File.Exists(bindingsFilePath))
{
// Try to process
try
{
SteamVR_Input_BindingFile bindingFile = JsonConvert.DeserializeObject(File.ReadAllText(bindingsFilePath));
// Find action list from our action set. If it's not present, create it:
if (!bindingFile.bindings.TryGetValue(ActionSetName, out SteamVR_Input_BindingFile_ActionList actionList))
{
actionList = new SteamVR_Input_BindingFile_ActionList();
bindingFile.bindings.Add(ActionSetName, actionList);
}
// Reset data
actionList.sources.Clear();
actionList.skeleton.Clear();
actionList.haptics.Clear();
// Try to process it. If it's been modified we need to save it back.
bool modified = false;
switch (bindingFile.controller_type)
{
case BindingControllerTypeOculusTouch:
modified = GenerateBindingsControllerOculusTouch(actionList);
break;
case BindingControllerTypeHtcVive:
modified = GenerateBindingsControllerHtcVive(actionList);
break;
case BindingControllerTypeHtcViveCosmos:
modified = GenerateBindingsControllerHtcViveCosmos(actionList);
break;
case BindingControllerTypeValveKnuckles:
modified = GenerateBindingsControllerValveKnuckles(actionList);
break;
case BindingControllerTypeWindowsMixedReality:
modified = GenerateBindingsControllerWindowsMixedReality(actionList);
break;
}
// Haptics, which are common
if (modified)
{
AddHapticsBindings(actionList);
}
// Need to Save?
if (modified)
{
File.WriteAllText(bindingsFilePath, JsonConvert.SerializeObject(bindingFile, Formatting.Indented));
}
}
catch (Exception e)
{
if (UxrGlobalSettings.Instance.LogLevelDevices >= UxrLogLevel.Errors)
{
Debug.LogError($"{UxrConstants.DevicesModule}: Error creating custom bindings to {nameof(SteamVR_Input_BindingFile)} in {bindingsFilePath} : {e.Message}, {e.StackTrace}");
}
}
}
}
}
///
/// Generates necessary custom bindings for the Oculus Touch controllers.
///
/// The action list to register the bindings in
/// True if the action list was modified
private static bool GenerateBindingsControllerOculusTouch(SteamVR_Input_BindingFile_ActionList actionList)
{
AddButtonBindings(actionList.sources, "system", UxrInputButtons.Menu, SideFlags.Left, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "x", UxrInputButtons.Button1, SideFlags.Left, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "y", UxrInputButtons.Button2, SideFlags.Left, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "a", UxrInputButtons.Button1, SideFlags.Right, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "b", UxrInputButtons.Button2, SideFlags.Right, ButtonUsageFlags.All);
AddVector2Bindings(actionList.sources, "joystick", BindingModeJoystick, UxrInput2D.Joystick, UxrInputButtons.Joystick, SideFlags.BothSides, ButtonUsageFlags.All);
AddVector1Bindings(actionList.sources, "grip", UxrInput1D.Grip, UxrInputButtons.Grip, SideFlags.BothSides, ButtonUsageFlags.All);
AddVector1Bindings(actionList.sources, "trigger", UxrInput1D.Trigger, UxrInputButtons.Trigger, SideFlags.BothSides, ButtonUsageFlags.All);
return true;
}
///
/// Generates necessary custom bindings for the HTC Vive controllers.
///
/// The action list to register the bindings in
/// True if the action list was modified
private static bool GenerateBindingsControllerHtcVive(SteamVR_Input_BindingFile_ActionList actionList)
{
AddButtonBindings(actionList.sources, "menu", UxrInputButtons.Menu, SideFlags.BothSides, ButtonUsageFlags.Click);
AddButtonBindings(actionList.sources, "grip", UxrInputButtons.Grip, SideFlags.BothSides, ButtonUsageFlags.Click);
AddVector2Bindings(actionList.sources, "trackpad", BindingModeTrackpad, UxrInput2D.Joystick, UxrInputButtons.Joystick, SideFlags.BothSides, ButtonUsageFlags.All);
AddVector1Bindings(actionList.sources, "trigger", UxrInput1D.Trigger, UxrInputButtons.Trigger, SideFlags.BothSides, ButtonUsageFlags.Click);
return true;
}
///
/// Generates necessary custom bindings for the HTC Vive Cosmos controllers.
///
/// The action list to register the bindings in
/// True if the action list was modified
private static bool GenerateBindingsControllerHtcViveCosmos(SteamVR_Input_BindingFile_ActionList actionList)
{
AddButtonBindings(actionList.sources, "system", UxrInputButtons.Menu, SideFlags.BothSides, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "x", UxrInputButtons.Button1, SideFlags.Left, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "y", UxrInputButtons.Button2, SideFlags.Left, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "a", UxrInputButtons.Button1, SideFlags.Right, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "b", UxrInputButtons.Button2, SideFlags.Right, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "bumper", UxrInputButtons.Bumper, SideFlags.BothSides, ButtonUsageFlags.All);
AddVector2Bindings(actionList.sources, "joystick", BindingModeJoystick, UxrInput2D.Joystick, UxrInputButtons.Joystick, SideFlags.BothSides, ButtonUsageFlags.All);
AddVector1Bindings(actionList.sources, "grip", UxrInput1D.Grip, UxrInputButtons.Grip, SideFlags.BothSides, ButtonUsageFlags.All);
AddVector1Bindings(actionList.sources, "trigger", UxrInput1D.Trigger, UxrInputButtons.Trigger, SideFlags.BothSides, ButtonUsageFlags.All);
return true;
}
///
/// Generates necessary custom bindings for the Valve Knuckles controllers.
///
/// The action list to register the bindings in
/// True if the action list was modified
private static bool GenerateBindingsControllerValveKnuckles(SteamVR_Input_BindingFile_ActionList actionList)
{
AddButtonBindings(actionList.sources, "system", UxrInputButtons.Menu, SideFlags.BothSides, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "a", UxrInputButtons.Button1, SideFlags.BothSides, ButtonUsageFlags.All);
AddButtonBindings(actionList.sources, "b", UxrInputButtons.Button2, SideFlags.BothSides, ButtonUsageFlags.All);
AddVector2Bindings(actionList.sources, "thumbstick", BindingModeJoystick, UxrInput2D.Joystick, UxrInputButtons.Joystick, SideFlags.BothSides, ButtonUsageFlags.All);
AddVector2Bindings(actionList.sources, "trackpad", BindingModeJoystick, UxrInput2D.Joystick2, UxrInputButtons.Joystick2, SideFlags.BothSides, ButtonUsageFlags.Touch);
AddVector1Bindings(actionList.sources, "grip", UxrInput1D.Grip, UxrInputButtons.Grip, SideFlags.BothSides, ButtonUsageFlags.Touch);
AddVector1Bindings(actionList.sources, "trigger", UxrInput1D.Trigger, UxrInputButtons.Trigger, SideFlags.BothSides, ButtonUsageFlags.All);
AddSkeletalBindings(actionList);
return true;
}
///
/// Generates necessary custom bindings for the Windows Mixed Reality controllers.
///
/// The action list to register the bindings in
/// True if the action list was modified
private static bool GenerateBindingsControllerWindowsMixedReality(SteamVR_Input_BindingFile_ActionList actionList)
{
AddButtonBindings(actionList.sources, "menu", UxrInputButtons.Menu, SideFlags.BothSides, ButtonUsageFlags.Click);
AddButtonBindings(actionList.sources, "grip", UxrInputButtons.Button1, SideFlags.BothSides, ButtonUsageFlags.Click);
AddVector2Bindings(actionList.sources, "joystick", BindingModeJoystick, UxrInput2D.Joystick, UxrInputButtons.Joystick, SideFlags.BothSides, ButtonUsageFlags.Click);
AddVector2Bindings(actionList.sources, "trackpad", BindingModeJoystick, UxrInput2D.Joystick2, UxrInputButtons.Joystick2, SideFlags.BothSides, ButtonUsageFlags.All);
AddVector1Bindings(actionList.sources, "trigger", UxrInput1D.Trigger, UxrInputButtons.Trigger, SideFlags.BothSides, ButtonUsageFlags.None);
return true;
}
///
/// Adds necessary binding information for a controller button.
///
/// List to add the necessary information to
/// Name of the device button in SteamVR to generate the binding for
/// UltimateXR button that it will be mapped to
///
/// Flags telling which sides need to be registered. Some buttons may be
/// available for both sides, some only for one side (menu, system...)
///
/// Which actions to register (click, touch...)
private static void AddButtonBindings(List sources,
string deviceElement,
UxrInputButtons button,
SideFlags sideFlags,
ButtonUsageFlags usageFlags)
{
if (sideFlags.HasFlag(SideFlags.Left))
{
sources.Add(GetControllerButtonBindingSource(deviceElement, BindingLeft, button, usageFlags));
}
if (sideFlags.HasFlag(SideFlags.Right))
{
sources.Add(GetControllerButtonBindingSource(deviceElement, BindingRight, button, usageFlags));
}
}
///
/// Adds necessary binding information for a vector1 controller element, which also can be used as a button.
///
/// List to add the necessary information to
/// Name of the device element in SteamVR to generate the binding for
/// The UltimateXR 1D controller that it will be mapped to
/// UltimateXR button that it will be mapped to
///
/// Flags telling which sides need to be registered. Some buttons may be available for both sides,
/// some only for one side
///
/// Which actions to register (click, touch...)
private static void AddVector1Bindings(List sources,
string deviceElement,
UxrInput1D input1D,
UxrInputButtons button,
SideFlags sideFlags,
ButtonUsageFlags buttonUsageFlags)
{
if (sideFlags.HasFlag(SideFlags.Left))
{
// Create button
SteamVR_Input_BindingFile_Source source = GetControllerButtonBindingSource(deviceElement, BindingLeft, button, buttonUsageFlags);
// Override mode with trigger and add trigger action
source.mode = BindingModeTrigger;
source.inputs.Add(BindingInputPull, GetVector1BindingDictionary(input1D));
sources.Add(source);
}
if (sideFlags.HasFlag(SideFlags.Right))
{
// Create button
SteamVR_Input_BindingFile_Source source = GetControllerButtonBindingSource(deviceElement, BindingRight, button, buttonUsageFlags);
// Override mode with trigger and add trigger action
source.mode = BindingModeTrigger;
source.inputs.Add(BindingInputPull, GetVector1BindingDictionary(input1D));
sources.Add(source);
}
}
///
/// Adds necessary binding information for a vector2 controller element, which also can be used as a button.
///
/// List to add the necessary information to
/// Name of the device element in SteamVR to generate the binding for
/// Input bindingMode (trackpad, joystick). Use string constants!
/// The UltimateXR 2D controller that it will be mapped to
/// UltimateXR button that it will be mapped to
///
/// Flags telling which sides need to be registered. Some buttons may be available for both sides,
/// some only for one side
///
/// Which actions to register (click, touch...)
private static void AddVector2Bindings(List sources,
string deviceElement,
string bindingMode,
UxrInput2D input2D,
UxrInputButtons button,
SideFlags sideFlags,
ButtonUsageFlags buttonUsageFlags)
{
if (sideFlags.HasFlag(SideFlags.Left))
{
// Create button
SteamVR_Input_BindingFile_Source source = GetControllerButtonBindingSource(deviceElement, BindingLeft, button, buttonUsageFlags);
// Override mode and add position action
source.mode = bindingMode;
source.inputs.Add(BindingInputPosition, GetVector2BindingDictionary(input2D));
sources.Add(source);
}
if (sideFlags.HasFlag(SideFlags.Right))
{
// Create button
SteamVR_Input_BindingFile_Source source = GetControllerButtonBindingSource(deviceElement, BindingRight, button, buttonUsageFlags);
// Override mode and add position action
source.mode = bindingMode;
source.inputs.Add(BindingInputPosition, GetVector2BindingDictionary(input2D));
sources.Add(source);
}
}
///
/// Adds the skeleton bindings to the given action list. This will enable access to skeleton data.
///
/// Action list to add the bindings to
private static void AddSkeletalBindings(SteamVR_Input_BindingFile_ActionList actionList)
{
actionList.skeleton.Add(new SteamVR_Input_BindingFile_Skeleton
{
output = $"/actions/{UxrSteamVRConstants.ActionSetName}/in/{UxrSteamVRConstants.ActionNameHandSkeletonLeft}",
path = BindingSkeletonPathLeft
});
actionList.skeleton.Add(new SteamVR_Input_BindingFile_Skeleton
{
output = $"/actions/{UxrSteamVRConstants.ActionSetName}/in/{UxrSteamVRConstants.ActionNameHandSkeletonRight}",
path = BindingSkeletonPathRight
});
}
///
/// Adds the haptic bindings to the given action list. This will enable sending haptic feedback.
///
/// Action list to add the bindings to
private static void AddHapticsBindings(SteamVR_Input_BindingFile_ActionList actionList)
{
actionList.haptics.Add(new SteamVR_Input_BindingFile_Haptic
{
output = $"/actions/{UxrSteamVRConstants.ActionSetName}/out/{UxrSteamVRConstants.ActionNameHandHaptics}",
path = BindingHapticsPathLeft
});
actionList.haptics.Add(new SteamVR_Input_BindingFile_Haptic
{
output = $"/actions/{UxrSteamVRConstants.ActionSetName}/out/{UxrSteamVRConstants.ActionNameHandHaptics}",
path = BindingHapticsPathRight
});
}
///
/// Creates a object describing a binding for a specific SteamVR
/// controller button of one side, to an well-known action.
///
/// SteamVR device element name
/// Left or right side. Use string constants.
/// UltimateXR button mapped to
/// Which actions to register (click, touch...)
/// object
private static SteamVR_Input_BindingFile_Source GetControllerButtonBindingSource(string deviceElement,
string side,
UxrInputButtons button,
ButtonUsageFlags usageFlags)
{
SteamVR_Input_BindingFile_Source source = new SteamVR_Input_BindingFile_Source
{
path = GetControllerBindingSourcePath(side, deviceElement),
mode = BindingModeButton
};
if (usageFlags.HasFlag(ButtonUsageFlags.Click))
{
source.inputs.Add(UxrSteamVRConstants.BindingInputClick, GetButtonBindingDictionary(button, UxrSteamVRConstants.BindingInputClick));
}
if (usageFlags.HasFlag(ButtonUsageFlags.Touch))
{
source.inputs.Add(UxrSteamVRConstants.BindingInputTouch, GetButtonBindingDictionary(button, UxrSteamVRConstants.BindingInputTouch));
}
return source;
}
///
/// Gets a string that can be used as a path to a given SteamVR device element.
///
/// Left or right side. Use string constants.
/// The SteamVR controller element name
/// Full path that can be used in a object
private static string GetControllerBindingSourcePath(string side, string deviceElement)
{
return $"/user/hand/{side}/input/{deviceElement}";
}
///
/// Gets a string that describes a path to a given button action.
///
/// UltimateXR button that the action will be mapped to
/// The type of input (click, touch...)
/// Full action path
private static string GetControllerBindingOutputButton(UxrInputButtons button, string inputType)
{
return $"/actions/{UxrSteamVRConstants.ActionSetName}/in/{button.ToString().ToLower()}_{inputType}_{UxrSteamVRConstants.BindingVarBool}";
}
///
/// Gets a string that describes a path to a given vector1 action.
///
/// UltimateXR input1D that the action will be mapped to
/// Full action path
private static string GetControllerBindingOutputInput1D(UxrInput1D input1D)
{
return $"/actions/{UxrSteamVRConstants.ActionSetName}/in/{input1D.ToString().ToLower()}_{UxrSteamVRConstants.BindingVarVector1}";
}
///
/// Gets a string that describes a path to a given vector2 action.
///
/// UltimateXR input2D that the action will be mapped to
/// Full action path
private static string GetControllerBindingOutputInput2D(UxrInput2D input2D)
{
return $"/actions/{UxrSteamVRConstants.ActionSetName}/in/{input2D.ToString().ToLower()}_{UxrSteamVRConstants.BindingVarVector2}";
}
///
/// Gets a dictionary describing a button input type to an action.
///
/// UltimateXR button to map
/// Input type (click, touch...)
/// Dictionary describing the mapping
private static SteamVR_Input_BindingFile_Source_Input_StringDictionary GetButtonBindingDictionary(UxrInputButtons button, string inputType)
{
return new SteamVR_Input_BindingFile_Source_Input_StringDictionary
{
{ BindingOutput, GetControllerBindingOutputButton(button, inputType) }
};
}
///
/// Gets a dictionary describing a vector1 type to an action.
///
/// UltimateXR input1D to map
/// Dictionary describing the mapping
private static SteamVR_Input_BindingFile_Source_Input_StringDictionary GetVector1BindingDictionary(UxrInput1D input1D)
{
return new SteamVR_Input_BindingFile_Source_Input_StringDictionary
{
{ BindingOutput, GetControllerBindingOutputInput1D(input1D) }
};
}
///
/// Gets a dictionary describing a vector2 type to an action.
///
/// UltimateXR input2D to map
/// Dictionary describing the mapping
private static SteamVR_Input_BindingFile_Source_Input_StringDictionary GetVector2BindingDictionary(UxrInput2D input2D)
{
return new SteamVR_Input_BindingFile_Source_Input_StringDictionary
{
{ BindingOutput, GetControllerBindingOutputInput2D(input2D) }
};
}
#endregion
#region Private Types & Data
private const string ActionSetName = "/actions/" + UxrSteamVRConstants.ActionSetName;
private const string BindingModeButton = "button";
private const string BindingModeTrigger = "trigger";
private const string BindingModeTrackpad = "trackpad";
private const string BindingModeJoystick = "joystick";
private const string BindingInputPull = "pull";
private const string BindingInputPosition = "position";
private const string BindingLeft = "left";
private const string BindingRight = "right";
private const string BindingOutput = "output";
private const string BindingSkeletonPathLeft = "/user/hand/left/input/skeleton/left";
private const string BindingSkeletonPathRight = "/user/hand/right/input/skeleton/right";
private const string BindingHapticsPathLeft = "/user/hand/left/output/haptic";
private const string BindingHapticsPathRight = "/user/hand/right/output/haptic";
private const string BindingControllerTypeOculusTouch = "oculus_touch";
private const string BindingControllerTypeHtcVive = "vive_controller";
private const string BindingControllerTypeHtcViveCosmos = "vive_cosmos_controller";
private const string BindingControllerTypeValveKnuckles = "knuckles";
private const string BindingControllerTypeWindowsMixedReality = "holographic_controller";
#endregion
}
#endif // ULTIMATEXR_USE_STEAMVR_SDK
}