//======= Copyright (c) Valve Corporation, All rights reserved. =============== using UnityEngine; using Valve.VR; using System.IO; using System; using System.Collections.Generic; using System.Reflection; using System.Linq; using Valve.Newtonsoft.Json; using System.Text; namespace Valve.VR { public partial class SteamVR_Input { public const string defaultInputGameObjectName = "[SteamVR Input]"; private const string localizationKeyName = "localization"; public static string actionsFilePath; /// True if the actions file has been initialized public static bool fileInitialized = false; /// True if the steamvr input system initialization process has completed successfully public static bool initialized = false; /// True if the preinitialization process (setting up dictionaries, etc) has completed successfully public static bool preInitialized = false; /// The serialized version of the actions file we're currently using (only used in editor) public static SteamVR_Input_ActionFile actionFile; /// The hash of the current action file on disk public static string actionFileHash; /// An event that fires when the non visual actions (everything except poses / skeletons) have been updated public static event Action onNonVisualActionsUpdated; /// An event that fires when the pose actions have been updated public static event PosesUpdatedHandler onPosesUpdated; public delegate void PosesUpdatedHandler(bool skipSendingEvents); /// An event that fires when the skeleton actions have been updated public static event SkeletonsUpdatedHandler onSkeletonsUpdated; public delegate void SkeletonsUpdatedHandler(bool skipSendingEvents); protected static bool initializing = false; protected static int startupFrame = 0; public static bool isStartupFrame { get { return Time.frameCount >= (startupFrame-1) && Time.frameCount <= (startupFrame+1); } } #region array accessors /// An array of all action sets public static SteamVR_ActionSet[] actionSets; /// An array of all actions (in all action sets) public static SteamVR_Action[] actions; /// An array of all input actions public static ISteamVR_Action_In[] actionsIn; /// An array of all output actions (haptic) public static ISteamVR_Action_Out[] actionsOut; /// An array of all the boolean actions public static SteamVR_Action_Boolean[] actionsBoolean; /// An array of all the single actions public static SteamVR_Action_Single[] actionsSingle; /// An array of all the vector2 actions public static SteamVR_Action_Vector2[] actionsVector2; /// An array of all the vector3 actions public static SteamVR_Action_Vector3[] actionsVector3; /// An array of all the pose actions public static SteamVR_Action_Pose[] actionsPose; /// An array of all the skeleton actions public static SteamVR_Action_Skeleton[] actionsSkeleton; /// An array of all the vibration (haptic) actions public static SteamVR_Action_Vibration[] actionsVibration; /// An array of all the input actions that are not pose or skeleton actions (boolean, single, vector2, vector3) public static ISteamVR_Action_In[] actionsNonPoseNonSkeletonIn; protected static Dictionary actionSetsByPath = new Dictionary(); protected static Dictionary actionSetsByPathLowered = new Dictionary(); protected static Dictionary actionsByPath = new Dictionary(); protected static Dictionary actionsByPathLowered = new Dictionary(); protected static Dictionary actionSetsByPathCache = new Dictionary(); protected static Dictionary actionsByPathCache = new Dictionary(); protected static Dictionary actionsByNameCache = new Dictionary(); protected static Dictionary actionSetsByNameCache = new Dictionary(); #endregion static SteamVR_Input() { #if !UNITY_EDITOR //If you want a single frame of performance increase on application start and have already generated your actions uncomment the following two lines //SteamVR_Actions.Preinitialize(); //return; #endif FindPreinitializeMethod(); } public static void ForcePreinitialize() { FindPreinitializeMethod(); } private static void FindPreinitializeMethod() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int assemblyIndex = 0; assemblyIndex < assemblies.Length; assemblyIndex++) { Assembly assembly = assemblies[assemblyIndex]; Type type = assembly.GetType(SteamVR_Input_Generator_Names.fullActionsClassName); if (type != null) { MethodInfo preinitMethodInfo = type.GetMethod(SteamVR_Input_Generator_Names.preinitializeMethodName); if (preinitMethodInfo != null) { preinitMethodInfo.Invoke(null, null); return; } } } } /// /// Get all the handles for actions and action sets. /// Initialize our dictionaries of action / action set names. /// Setup the tracking space universe origin /// public static void Initialize(bool force = false) { if (initialized == true && force == false) return; #if UNITY_EDITOR CheckSetup(); if (IsOpeningSetup()) return; #endif //Debug.Log("[SteamVR] Initializing SteamVR input..."); initializing = true; startupFrame = Time.frameCount; SteamVR_ActionSet_Manager.Initialize(); SteamVR_Input_Source.Initialize(); for (int actionIndex = 0; actionIndex < actions.Length; actionIndex++) { SteamVR_Action action = actions[actionIndex]; action.Initialize(true); } for (int actionSetIndex = 0; actionSetIndex < actionSets.Length; actionSetIndex++) { SteamVR_ActionSet set = actionSets[actionSetIndex]; set.Initialize(true); } if (SteamVR_Settings.instance.activateFirstActionSetOnStart) { if (actionSets.Length > 0) actionSets[0].Activate(); else { Debug.LogError("[SteamVR] No action sets to activate."); } } SteamVR_Action_Pose.SetTrackingUniverseOrigin(SteamVR_Settings.instance.trackingSpace); initialized = true; initializing = false; //Debug.Log("[SteamVR] Input initialization complete."); } public static void PreinitializeFinishActionSets() { for (int actionSetIndex = 0; actionSetIndex < actionSets.Length; actionSetIndex++) { SteamVR_ActionSet actionSet = actionSets[actionSetIndex]; actionSet.FinishPreInitialize(); } } public static void PreinitializeActionSetDictionaries() { actionSetsByPath.Clear(); actionSetsByPathLowered.Clear(); actionSetsByPathCache.Clear(); for (int actionSetIndex = 0; actionSetIndex < actionSets.Length; actionSetIndex++) { SteamVR_ActionSet actionSet = actionSets[actionSetIndex]; actionSetsByPath.Add(actionSet.fullPath, actionSet); actionSetsByPathLowered.Add(actionSet.fullPath.ToLower(), actionSet); } } public static void PreinitializeActionDictionaries() { actionsByPath.Clear(); actionsByPathLowered.Clear(); actionsByPathCache.Clear(); for (int actionIndex = 0; actionIndex < actions.Length; actionIndex++) { SteamVR_Action action = actions[actionIndex]; actionsByPath.Add(action.fullPath, action); actionsByPathLowered.Add(action.fullPath.ToLower(), action); } } /// Gets called by SteamVR_Behaviour every Update and updates actions if the steamvr settings are configured to update then. public static void Update() { if (initialized == false || isStartupFrame) return; if (SteamVR.settings.IsInputUpdateMode(SteamVR_UpdateModes.OnUpdate)) { UpdateNonVisualActions(); } if (SteamVR.settings.IsPoseUpdateMode(SteamVR_UpdateModes.OnUpdate)) { UpdateVisualActions(); } } /// /// Gets called by SteamVR_Behaviour every LateUpdate and updates actions if the steamvr settings are configured to update then. /// Also updates skeletons regardless of settings are configured to so we can account for animations on the skeletons. /// public static void LateUpdate() { if (initialized == false || isStartupFrame) return; if (SteamVR.settings.IsInputUpdateMode(SteamVR_UpdateModes.OnLateUpdate)) { UpdateNonVisualActions(); } if (SteamVR.settings.IsPoseUpdateMode(SteamVR_UpdateModes.OnLateUpdate)) { //update poses and skeleton UpdateVisualActions(); } else { //force skeleton update so animation blending sticks UpdateSkeletonActions(true); } } /// Gets called by SteamVR_Behaviour every FixedUpdate and updates actions if the steamvr settings are configured to update then. public static void FixedUpdate() { if (initialized == false || isStartupFrame) return; if (SteamVR.settings.IsInputUpdateMode(SteamVR_UpdateModes.OnFixedUpdate)) { UpdateNonVisualActions(); } if (SteamVR.settings.IsPoseUpdateMode(SteamVR_UpdateModes.OnFixedUpdate)) { UpdateVisualActions(); } } /// Gets called by SteamVR_Behaviour every OnPreCull and updates actions if the steamvr settings are configured to update then. public static void OnPreCull() { if (initialized == false || isStartupFrame) return; if (SteamVR.settings.IsInputUpdateMode(SteamVR_UpdateModes.OnPreCull)) { UpdateNonVisualActions(); } if (SteamVR.settings.IsPoseUpdateMode(SteamVR_UpdateModes.OnPreCull)) { UpdateVisualActions(); } } /// /// Updates the states of all the visual actions (pose / skeleton) /// /// Controls whether or not events are fired from this update call public static void UpdateVisualActions(bool skipStateAndEventUpdates = false) { if (initialized == false) return; SteamVR_ActionSet_Manager.UpdateActionStates(); UpdatePoseActions(skipStateAndEventUpdates); UpdateSkeletonActions(skipStateAndEventUpdates); } /// /// Updates the states of all the pose actions /// /// Controls whether or not events are fired from this update call public static void UpdatePoseActions(bool skipSendingEvents = false) { if (initialized == false) return; for (int actionIndex = 0; actionIndex < actionsPose.Length; actionIndex++) { SteamVR_Action_Pose action = actionsPose[actionIndex]; action.UpdateValues(skipSendingEvents); } if (onPosesUpdated != null) onPosesUpdated(false); } /// /// Updates the states of all the skeleton actions /// /// Controls whether or not events are fired from this update call public static void UpdateSkeletonActions(bool skipSendingEvents = false) { if (initialized == false) return; for (int actionIndex = 0; actionIndex < actionsSkeleton.Length; actionIndex++) { SteamVR_Action_Skeleton action = actionsSkeleton[actionIndex]; action.UpdateValue(skipSendingEvents); } if (onSkeletonsUpdated != null) onSkeletonsUpdated(skipSendingEvents); } /// /// Updates the states of all the non visual actions (boolean, single, vector2, vector3) /// public static void UpdateNonVisualActions() { if (initialized == false) return; SteamVR_ActionSet_Manager.UpdateActionStates(); for (int actionIndex = 0; actionIndex < actionsNonPoseNonSkeletonIn.Length; actionIndex++) { ISteamVR_Action_In action = actionsNonPoseNonSkeletonIn[actionIndex]; action.UpdateValues(); } if (onNonVisualActionsUpdated != null) onNonVisualActionsUpdated(); } #region String accessor helpers #region action accessors /// /// Get an action's action data by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static T GetActionDataFromPath(string path, bool caseSensitive = false) where T : SteamVR_Action_Source_Map { SteamVR_Action action = GetBaseActionFromPath(path, caseSensitive); if (action != null) { T actionData = (T)action.GetSourceMap(); return actionData; } return null; } /// /// Get an action set's data by the full path to that action. Action set paths are in the format /actions/[actionSet] /// /// The full path to the action you want (Action set paths are in the format /actions/[actionSet]) /// case sensitive searches are faster public static SteamVR_ActionSet_Data GetActionSetDataFromPath(string path, bool caseSensitive = false) { SteamVR_ActionSet actionSet = GetActionSetFromPath(path, caseSensitive); if (actionSet != null) { return actionSet.GetActionSetData(); } return null; } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static T GetActionFromPath(string path, bool caseSensitive = false, bool returnNulls = false) where T : SteamVR_Action, new() { SteamVR_Action foundAction = GetBaseActionFromPath(path, caseSensitive); if (foundAction != null) return foundAction.GetCopy(); if (returnNulls) return null; return CreateFakeAction(path, caseSensitive); } // non-copy version public static SteamVR_Action GetBaseActionFromPath(string path, bool caseSensitive = false) { if (string.IsNullOrEmpty(path)) return null; if (caseSensitive) { if (actionsByPath.ContainsKey(path)) { return actionsByPath[path]; } } else { if (actionsByPathCache.ContainsKey(path)) { return actionsByPathCache[path]; } else if (actionsByPath.ContainsKey(path)) { actionsByPathCache.Add(path, actionsByPath[path]); return actionsByPath[path]; } else { string loweredPath = path.ToLower(); if (actionsByPathLowered.ContainsKey(loweredPath)) { actionsByPathCache.Add(path, actionsByPathLowered[loweredPath]); return actionsByPath[loweredPath]; } else { actionsByPathCache.Add(path, null); } } } return null; } public static bool HasActionPath(string path, bool caseSensitive = false) { SteamVR_Action action = GetBaseActionFromPath(path, caseSensitive); return action != null; } public static bool HasAction(string actionName, bool caseSensitive = false) { SteamVR_Action action = GetBaseAction(null, actionName, caseSensitive); return action != null; } public static bool HasAction(string actionSetName, string actionName, bool caseSensitive = false) { SteamVR_Action action = GetBaseAction(actionSetName, actionName, caseSensitive); return action != null; } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Boolean GetBooleanActionFromPath(string path, bool caseSensitive = false) { return GetActionFromPath(path, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Single GetSingleActionFromPath(string path, bool caseSensitive = false) { return GetActionFromPath(path, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Vector2 GetVector2ActionFromPath(string path, bool caseSensitive = false) { return GetActionFromPath(path, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Vector3 GetVector3ActionFromPath(string path, bool caseSensitive = false) { return GetActionFromPath(path, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Vibration GetVibrationActionFromPath(string path, bool caseSensitive = false) { return GetActionFromPath(path, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Pose GetPoseActionFromPath(string path, bool caseSensitive = false) { return GetActionFromPath(path, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Skeleton GetSkeletonActionFromPath(string path, bool caseSensitive = false) { return GetActionFromPath(path, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster /// returns null if the action does not exist public static T GetAction(string actionSetName, string actionName, bool caseSensitive = false, bool returnNulls = false) where T : SteamVR_Action, new() { SteamVR_Action action = GetBaseAction(actionSetName, actionName, caseSensitive); if (action != null) return (T)action.GetCopy(); if (returnNulls) return null; return CreateFakeAction(actionSetName, actionName, caseSensitive); } public static SteamVR_Action GetBaseAction(string actionSetName, string actionName, bool caseSensitive = false) { if (actions == null) { return null; } if (string.IsNullOrEmpty(actionSetName)) { for (int actionIndex = 0; actionIndex < actions.Length; actionIndex++) { if (caseSensitive) { if (actions[actionIndex].GetShortName() == actionName) return actions[actionIndex]; } else { if (string.Equals(actions[actionIndex].GetShortName(), actionName, StringComparison.CurrentCultureIgnoreCase)) return actions[actionIndex]; } } } else { SteamVR_ActionSet actionSet = GetActionSet(actionSetName, caseSensitive, true); if (actionSet != null) { for (int actionIndex = 0; actionIndex < actionSet.allActions.Length; actionIndex++) { if (caseSensitive) { if (actionSet.allActions[actionIndex].GetShortName() == actionName) return actionSet.allActions[actionIndex]; } else { if (string.Equals(actionSet.allActions[actionIndex].GetShortName(), actionName, StringComparison.CurrentCultureIgnoreCase)) return actionSet.allActions[actionIndex]; } } } } return null; } private static T CreateFakeAction(string actionSetName, string actionName, bool caseSensitive) where T : SteamVR_Action, new() { if (typeof(T) == typeof(SteamVR_Action_Vibration)) { return SteamVR_Action.CreateUninitialized(actionSetName, SteamVR_ActionDirections.Out, actionName, caseSensitive); } else { return SteamVR_Action.CreateUninitialized(actionSetName, SteamVR_ActionDirections.In, actionName, caseSensitive); } } private static T CreateFakeAction(string actionPath, bool caseSensitive) where T : SteamVR_Action, new() { return SteamVR_Action.CreateUninitialized(actionPath, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static T GetAction(string actionName, bool caseSensitive = false) where T : SteamVR_Action, new() { return GetAction(null, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Boolean GetBooleanAction(string actionSetName, string actionName, bool caseSensitive = false) { return GetAction(actionSetName, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Boolean GetBooleanAction(string actionName, bool caseSensitive = false) { return GetAction(null, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Single GetSingleAction(string actionSetName, string actionName, bool caseSensitive = false) { return GetAction(actionSetName, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Single GetSingleAction(string actionName, bool caseSensitive = false) { return GetAction(null, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Vector2 GetVector2Action(string actionSetName, string actionName, bool caseSensitive = false) { return GetAction(actionSetName, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Vector2 GetVector2Action(string actionName, bool caseSensitive = false) { return GetAction(null, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Vector3 GetVector3Action(string actionSetName, string actionName, bool caseSensitive = false) { return GetAction(actionSetName, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Vector3 GetVector3Action(string actionName, bool caseSensitive = false) { return GetAction(null, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Pose GetPoseAction(string actionSetName, string actionName, bool caseSensitive = false) { return GetAction(actionSetName, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Pose GetPoseAction(string actionName, bool caseSensitive = false) { return GetAction(null, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Skeleton GetSkeletonAction(string actionSetName, string actionName, bool caseSensitive = false) { return GetAction(actionSetName, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Skeleton GetSkeletonAction(string actionName, bool caseSensitive = false) { return GetAction(null, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Vibration GetVibrationAction(string actionSetName, string actionName, bool caseSensitive = false) { return GetAction(actionSetName, actionName, caseSensitive); } /// /// Get an action by the full path to that action. Action paths are in the format /actions/[actionSet]/[direction]/[actionName] /// /// The type of action you're expecting to get back /// The full path to the action you want (Action paths are in the format /actions/[actionSet]/[direction]/[actionName]) /// case sensitive searches are faster public static SteamVR_Action_Vibration GetVibrationAction(string actionName, bool caseSensitive = false) { return GetAction(null, actionName, caseSensitive); } /// /// Get an action set by the full path to that action set. Action set paths are in the format /actions/[actionSet] /// /// The type of action set you're expecting to get back /// The name to the action set you want /// case sensitive searches are faster /// returns a null if the set does not exist public static T GetActionSet(string actionSetName, bool caseSensitive = false, bool returnNulls = false) where T : SteamVR_ActionSet, new() { if (actionSets == null) { if (returnNulls) return null; return SteamVR_ActionSet.CreateFromName(actionSetName); } for (int actionSetIndex = 0; actionSetIndex < actionSets.Length; actionSetIndex++) { if (caseSensitive) { if (actionSets[actionSetIndex].GetShortName() == actionSetName) return actionSets[actionSetIndex].GetCopy(); } else { if (string.Equals(actionSets[actionSetIndex].GetShortName(), actionSetName, StringComparison.CurrentCultureIgnoreCase)) return actionSets[actionSetIndex].GetCopy(); } } if (returnNulls) return null; return SteamVR_ActionSet.CreateFromName(actionSetName); } /// /// Get an action set by the full path to that action set. Action set paths are in the format /actions/[actionSet] /// /// The type of action set you're expecting to get back /// The name to the action set you want /// case sensitive searches are faster public static SteamVR_ActionSet GetActionSet(string actionSetName, bool caseSensitive = false, bool returnsNulls = false) { return GetActionSet(actionSetName, caseSensitive, returnsNulls); } protected static bool HasActionSet(string name, bool caseSensitive = false) { SteamVR_ActionSet actionSet = GetActionSet(name, caseSensitive, true); return actionSet != null; } /// /// Get an action set by the full path to that action set. Action set paths are in the format /actions/[actionSet] /// /// The type of action set you're expecting to get back /// The full path to the action set you want (Action paths are in the format /actions/[actionSet]) /// case sensitive searches are faster public static T GetActionSetFromPath(string path, bool caseSensitive = false, bool returnsNulls = false) where T : SteamVR_ActionSet, new() { if (actionSets == null || actionSets[0] == null || string.IsNullOrEmpty(path)) { if (returnsNulls) return null; return SteamVR_ActionSet.Create(path); } if (caseSensitive) { if (actionSetsByPath.ContainsKey(path)) { return actionSetsByPath[path].GetCopy(); } } else { if (actionSetsByPathCache.ContainsKey(path)) { SteamVR_ActionSet set = actionSetsByPathCache[path]; if (set == null) return null; else return set.GetCopy(); } else if (actionSetsByPath.ContainsKey(path)) { actionSetsByPathCache.Add(path, actionSetsByPath[path]); return actionSetsByPath[path].GetCopy(); } else { string loweredPath = path.ToLower(); if (actionSetsByPathLowered.ContainsKey(loweredPath)) { actionSetsByPathCache.Add(path, actionSetsByPathLowered[loweredPath]); return actionSetsByPath[loweredPath].GetCopy(); } else { actionSetsByPathCache.Add(path, null); } } } if (returnsNulls) return null; return SteamVR_ActionSet.Create(path); } /// /// Get an action set by the full path to that action set. Action set paths are in the format /actions/[actionSet] /// /// The full path to the action set you want (Action paths are in the format /actions/[actionSet]) /// case sensitive searches are faster public static SteamVR_ActionSet GetActionSetFromPath(string path, bool caseSensitive = false) { return GetActionSetFromPath(path, caseSensitive); } #endregion #region digital string accessors /// /// Get the state of an action by the action set name, action name, and input source. Optionally case sensitive (for faster results) /// /// The name of the action set the action is contained in /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) public static bool GetState(string actionSet, string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { SteamVR_Action_Boolean booleanAction = GetAction(actionSet, action, caseSensitive); if (booleanAction != null) { return booleanAction.GetState(inputSource); } return false; } /// /// Get the state of an action by the action name and input source. Optionally case sensitive (for faster results) /// /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) public static bool GetState(string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { return GetState(null, action, inputSource, caseSensitive); } /// /// Get the state down of an action by the action set name, action name, and input source. Optionally case sensitive (for faster results) /// /// The name of the action set the action is contained in /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) /// True when the action was false last update and is now true. Returns false again afterwards. public static bool GetStateDown(string actionSet, string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { SteamVR_Action_Boolean booleanAction = GetAction(actionSet, action, caseSensitive); if (booleanAction != null) { return booleanAction.GetStateDown(inputSource); } return false; } /// /// Get the state down of an action by the action name and input source. Optionally case sensitive (for faster results) /// /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) /// True when the action was false last update and is now true. Returns false again afterwards. public static bool GetStateDown(string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { return GetStateDown(null, action, inputSource, caseSensitive); } /// /// Get the state up of an action by the action set name, action name, and input source. Optionally case sensitive (for faster results) /// /// The name of the action set the action is contained in /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) /// True when the action was true last update and is now false. Returns false again afterwards. public static bool GetStateUp(string actionSet, string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { SteamVR_Action_Boolean booleanAction = GetAction(actionSet, action, caseSensitive); if (booleanAction != null) { return booleanAction.GetStateUp(inputSource); } return false; } /// /// Get the state up of an action by the action name and input source. Optionally case sensitive (for faster results) /// /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) /// True when the action was true last update and is now false. Returns false again afterwards. public static bool GetStateUp(string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { return GetStateDown(null, action, inputSource, caseSensitive); } #endregion #region analog string accessors /// /// Get the float value of an action by the action set name, action name, and input source. Optionally case sensitive (for faster results). (same as GetSingle) /// /// The name of the action set the action is contained in /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) public static float GetFloat(string actionSet, string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { SteamVR_Action_Single singleAction = GetAction(actionSet, action, caseSensitive); if (singleAction != null) { return singleAction.GetAxis(inputSource); } return 0; } /// /// Get the float value of an action by the action name and input source. Optionally case sensitive (for faster results). (same as GetSingle) /// /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) public static float GetFloat(string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { return GetFloat(null, action, inputSource, caseSensitive); } /// /// Get the float value of an action by the action set name, action name, and input source. Optionally case sensitive (for faster results). (same as GetFloat) /// /// The name of the action set the action is contained in /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) public static float GetSingle(string actionSet, string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { SteamVR_Action_Single singleAction = GetAction(actionSet, action, caseSensitive); if (singleAction != null) { return singleAction.GetAxis(inputSource); } return 0; } /// /// Get the float value of an action by the action name and input source. Optionally case sensitive (for faster results). (same as GetFloat) /// /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) public static float GetSingle(string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { return GetFloat(null, action, inputSource, caseSensitive); } /// /// Get the Vector2 value of an action by the action set name, action name, and input source. Optionally case sensitive (for faster results) /// /// The name of the action set the action is contained in /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) public static Vector2 GetVector2(string actionSet, string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { SteamVR_Action_Vector2 vectorAction = GetAction(actionSet, action, caseSensitive); if (vectorAction != null) { return vectorAction.GetAxis(inputSource); } return Vector2.zero; } /// /// Get the Vector2 value of an action by the action name and input source. Optionally case sensitive (for faster results) /// /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) public static Vector2 GetVector2(string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { return GetVector2(null, action, inputSource, caseSensitive); } /// /// Get the Vector3 value of an action by the action set name, action name, and input source. Optionally case sensitive (for faster results) /// /// The name of the action set the action is contained in /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) public static Vector3 GetVector3(string actionSet, string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { SteamVR_Action_Vector3 vectorAction = GetAction(actionSet, action, caseSensitive); if (vectorAction != null) { return vectorAction.GetAxis(inputSource); } return Vector3.zero; } /// /// Get the Vector3 value of an action by the action name and input source. Optionally case sensitive (for faster results) /// /// The name of the action to get the state of /// The input source to get the action state from /// Whether or not the action set and action name searches should be case sensitive (case sensitive searches are faster) public static Vector3 GetVector3(string action, SteamVR_Input_Sources inputSource, bool caseSensitive = false) { return GetVector3(null, action, inputSource, caseSensitive); } #endregion #endregion /// /// Returns all of the action sets. If we're in the editor, doesn't rely on the actionSets field being filled. /// public static SteamVR_ActionSet[] GetActionSets() { return actionSets; } /// /// Returns all of the actions of the specified type. If we're in the editor, doesn't rely on the arrays being filled. /// /// The type of actions you want to get public static T[] GetActions() where T : SteamVR_Action { Type type = typeof(T); if (type == typeof(SteamVR_Action)) { return actions as T[]; } else if (type == typeof(ISteamVR_Action_In)) { return actionsIn as T[]; } else if (type == typeof(ISteamVR_Action_Out)) { return actionsOut as T[]; } else if (type == typeof(SteamVR_Action_Boolean)) { return actionsBoolean as T[]; } else if (type == typeof(SteamVR_Action_Single)) { return actionsSingle as T[]; } else if (type == typeof(SteamVR_Action_Vector2)) { return actionsVector2 as T[]; } else if (type == typeof(SteamVR_Action_Vector3)) { return actionsVector3 as T[]; } else if (type == typeof(SteamVR_Action_Pose)) { return actionsPose as T[]; } else if (type == typeof(SteamVR_Action_Skeleton)) { return actionsSkeleton as T[]; } else if (type == typeof(SteamVR_Action_Vibration)) { return actionsVibration as T[]; } else { Debug.Log("[SteamVR] Wrong type."); } return null; } /// /// Gets the localized name of the device that the action corresponds to. /// /// /// /// /// VRInputString_Hand - Which hand the origin is in. E.g. "Left Hand" /// VRInputString_ControllerType - What kind of controller the user has in that hand.E.g. "Vive Controller" /// VRInputString_InputSource - What part of that controller is the origin. E.g. "Trackpad" /// VRInputString_All - All of the above. E.g. "Left Hand Vive Controller Trackpad" /// /// public static string GetLocalizedName(ulong originHandle, params EVRInputStringBits[] localizedParts) { int localizedPartsMask = 0; for (int partIndex = 0; partIndex < localizedParts.Length; partIndex++) localizedPartsMask |= (int)localizedParts[partIndex]; StringBuilder stringBuilder = new StringBuilder(500); OpenVR.Input.GetOriginLocalizedName(originHandle, stringBuilder, 500, localizedPartsMask); return stringBuilder.ToString(); } /// Tell SteamVR that we're using the actions file at the path defined in SteamVR_Settings. public static void IdentifyActionsFile(bool showLogs = true) { string currentPath = Application.dataPath; int lastIndex = currentPath.LastIndexOf('/'); currentPath = currentPath.Remove(lastIndex, currentPath.Length - lastIndex); string fullPath = System.IO.Path.Combine(currentPath, SteamVR_Settings.instance.actionsFilePath); fullPath = fullPath.Replace("\\", "/"); if (File.Exists(fullPath)) { EVRInputError err = OpenVR.Input.SetActionManifestPath(fullPath); if (err != EVRInputError.None) Debug.LogError("[SteamVR] Error loading action manifest into SteamVR: " + err.ToString()); else { int numActions = 0; if (SteamVR_Input.actions != null) { numActions = SteamVR_Input.actions.Length; if (showLogs) Debug.Log(string.Format("[SteamVR] Successfully loaded {0} actions from action manifest into SteamVR ({1})", numActions, fullPath)); } else { if (showLogs) Debug.LogWarning("[SteamVR] No actions found, but the action manifest was loaded. This usually means you haven't generated actions. Window -> SteamVR Input -> Save and Generate."); } } } else { if (showLogs) Debug.LogError("[SteamVR] Could not find actions file at: " + fullPath); } } /// /// Does the actions file in memory differ from the one on disk as determined by a md5 hash /// public static bool HasFileInMemoryBeenModified() { string projectPath = Application.dataPath; int lastIndex = projectPath.LastIndexOf("/"); projectPath = projectPath.Remove(lastIndex, projectPath.Length - lastIndex); actionsFilePath = Path.Combine(projectPath, SteamVR_Settings.instance.actionsFilePath); string jsonText = null; if (File.Exists(actionsFilePath)) { jsonText = System.IO.File.ReadAllText(actionsFilePath); } else { return true; } string newHashFromFile = SteamVR_Utils.GetBadMD5Hash(jsonText); string newJSON = JsonConvert.SerializeObject(SteamVR_Input.actionFile, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); string newHashFromMemory = SteamVR_Utils.GetBadMD5Hash(newJSON); return newHashFromFile != newHashFromMemory; } public static bool CreateEmptyActionsFile(bool completelyEmpty = false) { string projectPath = Application.dataPath; int lastIndex = projectPath.LastIndexOf("/"); projectPath = projectPath.Remove(lastIndex, projectPath.Length - lastIndex); actionsFilePath = Path.Combine(projectPath, SteamVR_Settings.instance.actionsFilePath); if (File.Exists(actionsFilePath)) { Debug.LogErrorFormat("[SteamVR] Actions file already exists in project root: {0}", actionsFilePath); return false; } actionFile = new SteamVR_Input_ActionFile(); if (completelyEmpty == false) { actionFile.action_sets.Add(SteamVR_Input_ActionFile_ActionSet.CreateNew()); actionFile.actions.Add(SteamVR_Input_ActionFile_Action.CreateNew(actionFile.action_sets[0].shortName, SteamVR_ActionDirections.In, SteamVR_Input_ActionFile_ActionTypes.boolean)); } string newJSON = JsonConvert.SerializeObject(actionFile, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); File.WriteAllText(actionsFilePath, newJSON); actionFile.InitializeHelperLists(); fileInitialized = true; return true; } public static bool DoesActionsFileExist() { string projectPath = Application.dataPath; int lastIndex = projectPath.LastIndexOf("/"); projectPath = projectPath.Remove(lastIndex, projectPath.Length - lastIndex); actionsFilePath = Path.Combine(projectPath, SteamVR_Settings.instance.actionsFilePath); return File.Exists(actionsFilePath); } /// /// Load from disk and deserialize the actions file /// /// Force a refresh of this file from disk public static bool InitializeFile(bool force = false, bool showErrors = true) { bool actionsFileExists = DoesActionsFileExist(); string jsonText = null; if (actionsFileExists) { jsonText = System.IO.File.ReadAllText(actionsFilePath); } else { if (showErrors) Debug.LogErrorFormat("[SteamVR] Actions file does not exist in project root: {0}", actionsFilePath); return false; } if (fileInitialized == true || (fileInitialized == true && force == false)) { string newHash = SteamVR_Utils.GetBadMD5Hash(jsonText); if (newHash == actionFileHash) { return true; } actionFileHash = newHash; } actionFile = Valve.Newtonsoft.Json.JsonConvert.DeserializeObject(jsonText); actionFile.InitializeHelperLists(); fileInitialized = true; return true; } /// /// Deletes the action manifest file and all the default bindings it had listed in the default bindings section /// /// True if we deleted an action file, false if not. public static bool DeleteManifestAndBindings() { if (DoesActionsFileExist() == false) return false; InitializeFile(); string[] filesToDelete = actionFile.GetFilesToCopy(); foreach (string bindingFilePath in filesToDelete) { FileInfo bindingFileInfo = new FileInfo(bindingFilePath); bindingFileInfo.IsReadOnly = false; File.Delete(bindingFilePath); } if (File.Exists(actionsFilePath)) { FileInfo actionFileInfo = new FileInfo(actionsFilePath); actionFileInfo.IsReadOnly = false; File.Delete(actionsFilePath); actionFile = null; fileInitialized = false; return true; } return false; } #if UNITY_EDITOR public static string GetResourcesFolderPath(bool fromAssetsDirectory = false) { string inputFolder = string.Format("Assets/{0}", SteamVR_Settings.instance.steamVRInputPath); string path = Path.Combine(inputFolder, "Resources"); bool createdDirectory = false; if (Directory.Exists(inputFolder) == false) { Directory.CreateDirectory(inputFolder); createdDirectory = true; } if (Directory.Exists(path) == false) { Directory.CreateDirectory(path); createdDirectory = true; } if (createdDirectory) UnityEditor.AssetDatabase.Refresh(); if (fromAssetsDirectory == false) return path.Replace("Assets/", ""); else return path; } private static bool checkingSetup = false; private static bool openingSetup = false; public static bool IsOpeningSetup() { return openingSetup; } private static void CheckSetup() { if (checkingSetup == false && openingSetup == false && (SteamVR_Input.actions == null || SteamVR_Input.actions.Length == 0)) { checkingSetup = true; Debug.Break(); bool open = UnityEditor.EditorUtility.DisplayDialog("[SteamVR]", "It looks like you haven't generated actions for SteamVR Input yet. Would you like to open the SteamVR Input window?", "Yes", "No"); if (open) { openingSetup = true; UnityEditor.EditorApplication.isPlaying = false; Type editorWindowType = FindType("Valve.VR.SteamVR_Input_EditorWindow"); if (editorWindowType != null) { var window = UnityEditor.EditorWindow.GetWindow(editorWindowType, false, "SteamVR Input", true); if (window != null) window.Show(); } } else { Debug.LogError("[SteamVR] This version of SteamVR will not work if you do not create and generate actions. Please open the SteamVR Input window or downgrade to version 1.2.3 (on github)"); } checkingSetup = false; } } private static Type FindType(string typeName) { var type = Type.GetType(typeName); if (type != null) return type; foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) { type = a.GetType(typeName); if (type != null) return type; } return null; } #endif } }