//======= Copyright (c) Valve Corporation, All rights reserved. =============== using UnityEngine; using System.Collections; using System; using Valve.VR; using System.Runtime.InteropServices; using System.Collections.Generic; namespace Valve.VR { [Serializable] /// /// Pose actions represent a position, rotation, and velocities inside the tracked space. /// SteamVR keeps a log of past poses so you can retrieve old poses with GetPoseAtTimeOffset or GetVelocitiesAtTimeOffset. /// You can also pass in times in the future to these methods for SteamVR's best prediction of where the pose will be at that time. /// public class SteamVR_Action_Pose : SteamVR_Action_Pose_Base, SteamVR_Action_Pose_Source>, ISerializationCallbackReceiver { public delegate void ActiveChangeHandler(SteamVR_Action_Pose fromAction, SteamVR_Input_Sources fromSource, bool active); public delegate void ChangeHandler(SteamVR_Action_Pose fromAction, SteamVR_Input_Sources fromSource); public delegate void UpdateHandler(SteamVR_Action_Pose fromAction, SteamVR_Input_Sources fromSource); public delegate void TrackingChangeHandler(SteamVR_Action_Pose fromAction, SteamVR_Input_Sources fromSource, ETrackingResult trackingState); public delegate void ValidPoseChangeHandler(SteamVR_Action_Pose fromAction, SteamVR_Input_Sources fromSource, bool validPose); public delegate void DeviceConnectedChangeHandler(SteamVR_Action_Pose fromAction, SteamVR_Input_Sources fromSource, bool deviceConnected); /// [Shortcut to: SteamVR_Input_Sources.Any] Event fires when the active state (ActionSet active and binding active) changes public event ActiveChangeHandler onActiveChange { add { sourceMap[SteamVR_Input_Sources.Any].onActiveChange += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onActiveChange -= value; } } /// [Shortcut to: SteamVR_Input_Sources.Any] Event fires when the active state of the binding changes public event ActiveChangeHandler onActiveBindingChange { add { sourceMap[SteamVR_Input_Sources.Any].onActiveBindingChange += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onActiveBindingChange -= value; } } /// [Shortcut to: SteamVR_Input_Sources.Any] Event fires when the orientation of the pose changes more than the changeTolerance public event ChangeHandler onChange { add { sourceMap[SteamVR_Input_Sources.Any].onChange += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onChange -= value; } } /// [Shortcut to: SteamVR_Input_Sources.Any] Event fires when the action is updated public event UpdateHandler onUpdate { add { sourceMap[SteamVR_Input_Sources.Any].onUpdate += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onUpdate -= value; } } /// [Shortcut to: SteamVR_Input_Sources.Any] Event fires when the state of the tracking has changed public event TrackingChangeHandler onTrackingChanged { add { sourceMap[SteamVR_Input_Sources.Any].onTrackingChanged += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onTrackingChanged -= value; } } /// [Shortcut to: SteamVR_Input_Sources.Any] Event fires when the validity of the pose has changed public event ValidPoseChangeHandler onValidPoseChanged { add { sourceMap[SteamVR_Input_Sources.Any].onValidPoseChanged += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onValidPoseChanged -= value; } } /// [Shortcut to: SteamVR_Input_Sources.Any] Event fires when the device bound to this pose is connected or disconnected public event DeviceConnectedChangeHandler onDeviceConnectedChanged { add { sourceMap[SteamVR_Input_Sources.Any].onDeviceConnectedChanged += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onDeviceConnectedChanged -= value; } } /// Fires an event when a device is connected or disconnected. /// The device you would like to add an event to. Any if the action is not device specific. /// The method you would like to be called when a device is connected. Should take a SteamVR_Action_Pose as a param public void AddOnDeviceConnectedChanged(SteamVR_Input_Sources inputSource, DeviceConnectedChangeHandler functionToCall) { sourceMap[inputSource].onDeviceConnectedChanged += functionToCall; } /// Stops executing the function setup by the corresponding AddListener /// The device you would like to remove an event from. Any if the action is not device specific. /// The method you would like to stop calling when a device is connected. Should take a SteamVR_Action_Pose as a param public void RemoveOnDeviceConnectedChanged(SteamVR_Input_Sources inputSource, DeviceConnectedChangeHandler functionToStopCalling) { sourceMap[inputSource].onDeviceConnectedChanged -= functionToStopCalling; } /// Fires an event when the tracking of the device has changed /// The device you would like to add an event to. Any if the action is not device specific. /// The method you would like to be called when tracking has changed. Should take a SteamVR_Action_Pose as a param public void AddOnTrackingChanged(SteamVR_Input_Sources inputSource, TrackingChangeHandler functionToCall) { sourceMap[inputSource].onTrackingChanged += functionToCall; } /// Stops executing the function setup by the corresponding AddListener /// The device you would like to remove an event from. Any if the action is not device specific. /// The method you would like to stop calling when tracking has changed. Should take a SteamVR_Action_Pose as a param public void RemoveOnTrackingChanged(SteamVR_Input_Sources inputSource, TrackingChangeHandler functionToStopCalling) { sourceMap[inputSource].onTrackingChanged -= functionToStopCalling; } /// Fires an event when the device now has a valid pose or no longer has a valid pose /// The device you would like to add an event to. Any if the action is not device specific. /// The method you would like to be called when the pose has become valid or invalid. Should take a SteamVR_Action_Pose as a param public void AddOnValidPoseChanged(SteamVR_Input_Sources inputSource, ValidPoseChangeHandler functionToCall) { sourceMap[inputSource].onValidPoseChanged += functionToCall; } /// Stops executing the function setup by the corresponding AddListener /// The device you would like to remove an event from. Any if the action is not device specific. /// The method you would like to stop calling when the pose has become valid or invalid. Should take a SteamVR_Action_Pose as a param public void RemoveOnValidPoseChanged(SteamVR_Input_Sources inputSource, ValidPoseChangeHandler functionToStopCalling) { sourceMap[inputSource].onValidPoseChanged -= functionToStopCalling; } /// Executes a function when this action's bound state changes /// The device you would like to get data from. Any if the action is not device specific. public void AddOnActiveChangeListener(SteamVR_Input_Sources inputSource, ActiveChangeHandler functionToCall) { sourceMap[inputSource].onActiveChange += functionToCall; } /// Stops executing the function setup by the corresponding AddListener /// The local function that you've setup to receive update events /// The device you would like to get data from. Any if the action is not device specific. public void RemoveOnActiveChangeListener(SteamVR_Input_Sources inputSource, ActiveChangeHandler functionToStopCalling) { sourceMap[inputSource].onActiveChange -= functionToStopCalling; } /// Executes a function when the state of this action (with the specified inputSource) changes /// A local function that receives the boolean action who's state has changed, the corresponding input source, and the new value /// The device you would like to get data from. Any if the action is not device specific. public void AddOnChangeListener(SteamVR_Input_Sources inputSource, ChangeHandler functionToCall) { sourceMap[inputSource].onChange += functionToCall; } /// Stops executing the function setup by the corresponding AddListener /// The local function that you've setup to receive on change events /// The device you would like to get data from. Any if the action is not device specific. public void RemoveOnChangeListener(SteamVR_Input_Sources inputSource, ChangeHandler functionToStopCalling) { sourceMap[inputSource].onChange -= functionToStopCalling; } /// Executes a function when the state of this action (with the specified inputSource) is updated. /// A local function that receives the boolean action who's state has changed, the corresponding input source, and the new value /// The device you would like to get data from. Any if the action is not device specific. public void AddOnUpdateListener(SteamVR_Input_Sources inputSource, UpdateHandler functionToCall) { sourceMap[inputSource].onUpdate += functionToCall; } /// Stops executing the function setup by the corresponding AddListener /// The local function that you've setup to receive update events /// The device you would like to get data from. Any if the action is not device specific. public void RemoveOnUpdateListener(SteamVR_Input_Sources inputSource, UpdateHandler functionToStopCalling) { sourceMap[inputSource].onUpdate -= functionToStopCalling; } /// /// Removes all listeners, useful for dispose pattern /// public void RemoveAllListeners(SteamVR_Input_Sources input_Sources) { sourceMap[input_Sources].RemoveAllListeners(); } void ISerializationCallbackReceiver.OnBeforeSerialize() { } void ISerializationCallbackReceiver.OnAfterDeserialize() { InitAfterDeserialize(); } /// /// Sets all pose and skeleton actions to use the specified universe origin. /// public static void SetTrackingUniverseOrigin(ETrackingUniverseOrigin newOrigin) { SetUniverseOrigin(newOrigin); OpenVR.Compositor.SetTrackingSpace(newOrigin); } } [Serializable] /// /// The base pose action (pose and skeleton inherit from this) /// public abstract class SteamVR_Action_Pose_Base : SteamVR_Action_In, ISteamVR_Action_Pose where SourceMap : SteamVR_Action_Pose_Source_Map, new() where SourceElement : SteamVR_Action_Pose_Source, new() { /// /// Sets all pose (and skeleton) actions to use the specified universe origin. /// protected static void SetUniverseOrigin(ETrackingUniverseOrigin newOrigin) { for (int actionIndex = 0; actionIndex < SteamVR_Input.actionsPose.Length; actionIndex++) { SteamVR_Input.actionsPose[actionIndex].sourceMap.SetTrackingUniverseOrigin(newOrigin); } for (int actionIndex = 0; actionIndex < SteamVR_Input.actionsSkeleton.Length; actionIndex++) { SteamVR_Input.actionsSkeleton[actionIndex].sourceMap.SetTrackingUniverseOrigin(newOrigin); } } /// [Shortcut to: SteamVR_Input_Sources.Any] The local position of this action relative to the universe origin public Vector3 localPosition { get { return sourceMap[SteamVR_Input_Sources.Any].localPosition; } } /// [Shortcut to: SteamVR_Input_Sources.Any] The local rotation of this action relative to the universe origin public Quaternion localRotation { get { return sourceMap[SteamVR_Input_Sources.Any].localRotation; } } /// [Shortcut to: SteamVR_Input_Sources.Any] The state of the tracking system that is used to create pose data (position, rotation, etc) public ETrackingResult trackingState { get { return sourceMap[SteamVR_Input_Sources.Any].trackingState; } } /// [Shortcut to: SteamVR_Input_Sources.Any] The local velocity of this pose relative to the universe origin public Vector3 velocity { get { return sourceMap[SteamVR_Input_Sources.Any].velocity; } } /// [Shortcut to: SteamVR_Input_Sources.Any] The local angular velocity of this pose relative to the universe origin public Vector3 angularVelocity { get { return sourceMap[SteamVR_Input_Sources.Any].angularVelocity; } } /// [Shortcut to: SteamVR_Input_Sources.Any] True if the pose retrieved for this action and input source is valid (good data from the tracking source) public bool poseIsValid { get { return sourceMap[SteamVR_Input_Sources.Any].poseIsValid; } } /// [Shortcut to: SteamVR_Input_Sources.Any] True if the device bound to this action and input source is connected public bool deviceIsConnected { get { return sourceMap[SteamVR_Input_Sources.Any].deviceIsConnected; } } /// [Shortcut to: SteamVR_Input_Sources.Any] The local position for this pose during the previous update public Vector3 lastLocalPosition { get { return sourceMap[SteamVR_Input_Sources.Any].lastLocalPosition; } } /// [Shortcut to: SteamVR_Input_Sources.Any] The local rotation for this pose during the previous update public Quaternion lastLocalRotation { get { return sourceMap[SteamVR_Input_Sources.Any].lastLocalRotation; } } /// [Shortcut to: SteamVR_Input_Sources.Any] The tracking state for this pose during the previous update public ETrackingResult lastTrackingState { get { return sourceMap[SteamVR_Input_Sources.Any].lastTrackingState; } } /// [Shortcut to: SteamVR_Input_Sources.Any] The velocity for this pose during the previous update public Vector3 lastVelocity { get { return sourceMap[SteamVR_Input_Sources.Any].lastVelocity; } } /// [Shortcut to: SteamVR_Input_Sources.Any] The angular velocity for this pose during the previous update public Vector3 lastAngularVelocity { get { return sourceMap[SteamVR_Input_Sources.Any].lastAngularVelocity; } } /// [Shortcut to: SteamVR_Input_Sources.Any] True if the pose was valid during the previous update public bool lastPoseIsValid { get { return sourceMap[SteamVR_Input_Sources.Any].lastPoseIsValid; } } /// [Shortcut to: SteamVR_Input_Sources.Any] True if the device bound to this action was connected during the previous update public bool lastDeviceIsConnected { get { return sourceMap[SteamVR_Input_Sources.Any].lastDeviceIsConnected; } } public SteamVR_Action_Pose_Base() { } /// /// [Should not be called by user code] /// Updates the data for all the input sources the system has detected need to be updated. /// public virtual void UpdateValues(bool skipStateAndEventUpdates) { sourceMap.UpdateValues(skipStateAndEventUpdates); } /// /// SteamVR keeps a log of past poses so you can retrieve old poses or estimated poses in the future by passing in a secondsFromNow value that is negative or positive. /// /// The device you would like to get data from. Any if the action is not device specific. /// The time offset in the future (estimated) or in the past (previously recorded) you want to get data from /// true if the call succeeded public bool GetVelocitiesAtTimeOffset(SteamVR_Input_Sources inputSource, float secondsFromNow, out Vector3 velocity, out Vector3 angularVelocity) { return sourceMap[inputSource].GetVelocitiesAtTimeOffset(secondsFromNow, out velocity, out angularVelocity); } /// /// SteamVR keeps a log of past poses so you can retrieve old poses or estimated poses in the future by passing in a secondsFromNow value that is negative or positive. /// /// The device you would like to get data from. Any if the action is not device specific. /// The time offset in the future (estimated) or in the past (previously recorded) you want to get data from /// true if the call succeeded public bool GetPoseAtTimeOffset(SteamVR_Input_Sources inputSource, float secondsFromNow, out Vector3 localPosition, out Quaternion localRotation, out Vector3 velocity, out Vector3 angularVelocity) { return sourceMap[inputSource].GetPoseAtTimeOffset(secondsFromNow, out localPosition, out localRotation, out velocity, out angularVelocity); } /// /// Update a transform's local position and local roation to match the pose from the most recent update /// /// The device you would like to get data from. Any if the action is not device specific. /// The transform of the object to be updated public virtual void UpdateTransform(SteamVR_Input_Sources inputSource, Transform transformToUpdate) { sourceMap[inputSource].UpdateTransform(transformToUpdate); } /// The local position of this action relative to the universe origin /// The device you would like to get data from. Any if the action is not device specific. public Vector3 GetLocalPosition(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].localPosition; } /// The local rotation of this action relative to the universe origin /// The device you would like to get data from. Any if the action is not device specific. public Quaternion GetLocalRotation(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].localRotation; } /// The local velocity of this pose relative to the universe origin /// The device you would like to get data from. Any if the action is not device specific. public Vector3 GetVelocity(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].velocity; } /// The local angular velocity of this pose relative to the universe origin /// The device you would like to get data from. Any if the action is not device specific. public Vector3 GetAngularVelocity(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].angularVelocity; } /// True if the device bound to this action and input source is connected /// The device you would like to get data from. Any if the action is not device specific. public bool GetDeviceIsConnected(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].deviceIsConnected; } /// True if the pose retrieved for this action and input source is valid (good data from the tracking source) /// The device you would like to get data from. Any if the action is not device specific. public bool GetPoseIsValid(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].poseIsValid; } /// The state of the tracking system that is used to create pose data (position, rotation, etc) /// The device you would like to get data from. Any if the action is not device specific. public ETrackingResult GetTrackingResult(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].trackingState; } /// The local position for this pose during the previous update /// The device you would like to get data from. Any if the action is not device specific. public Vector3 GetLastLocalPosition(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].lastLocalPosition; } /// The local rotation for this pose during the previous update /// The device you would like to get data from. Any if the action is not device specific. public Quaternion GetLastLocalRotation(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].lastLocalRotation; } /// The velocity for this pose during the previous update /// The device you would like to get data from. Any if the action is not device specific. public Vector3 GetLastVelocity(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].lastVelocity; } /// The angular velocity for this pose during the previous update /// The device you would like to get data from. Any if the action is not device specific. public Vector3 GetLastAngularVelocity(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].lastAngularVelocity; } /// True if the device bound to this action was connected during the previous update /// The device you would like to get data from. Any if the action is not device specific. public bool GetLastDeviceIsConnected(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].lastDeviceIsConnected; } /// True if the pose was valid during the previous update /// The device you would like to get data from. Any if the action is not device specific. public bool GetLastPoseIsValid(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].lastPoseIsValid; } /// The tracking state for this pose during the previous update /// The device you would like to get data from. Any if the action is not device specific. public ETrackingResult GetLastTrackingResult(SteamVR_Input_Sources inputSource) { return sourceMap[inputSource].lastTrackingState; } } /// /// Boolean actions are either true or false. There is an onStateUp and onStateDown event for the rising and falling edge. /// public class SteamVR_Action_Pose_Source_Map : SteamVR_Action_In_Source_Map where Source : SteamVR_Action_Pose_Source, new() { /// /// Sets all pose (and skeleton) actions to use the specified universe origin without going through the sourcemap indexer /// public void SetTrackingUniverseOrigin(ETrackingUniverseOrigin newOrigin) { for (int sourceIndex = 0; sourceIndex < sources.Length; sourceIndex++) { if (sources[sourceIndex] != null) sources[sourceIndex].universeOrigin = newOrigin; } } public virtual void UpdateValues(bool skipStateAndEventUpdates) { for (int sourceIndex = 0; sourceIndex < updatingSources.Count; sourceIndex++) { sources[updatingSources[sourceIndex]].UpdateValue(skipStateAndEventUpdates); } } } public class SteamVR_Action_Pose_Source : SteamVR_Action_In_Source, ISteamVR_Action_Pose { public ETrackingUniverseOrigin universeOrigin = ETrackingUniverseOrigin.TrackingUniverseRawAndUncalibrated; protected static uint poseActionData_size = 0; /// The distance the pose needs to move/rotate before a change is detected public float changeTolerance = Mathf.Epsilon; /// Event fires when the active state (ActionSet active and binding active) changes public event SteamVR_Action_Pose.ActiveChangeHandler onActiveChange; /// Event fires when the active state of the binding changes public event SteamVR_Action_Pose.ActiveChangeHandler onActiveBindingChange; /// Event fires when the orientation of the pose changes more than the changeTolerance public event SteamVR_Action_Pose.ChangeHandler onChange; /// Event fires when the action is updated public event SteamVR_Action_Pose.UpdateHandler onUpdate; /// Event fires when the state of the tracking system that is used to create pose data (position, rotation, etc) changes public event SteamVR_Action_Pose.TrackingChangeHandler onTrackingChanged; /// Event fires when the state of the pose data retrieved for this action changes validity (good/bad data from the tracking source) public event SteamVR_Action_Pose.ValidPoseChangeHandler onValidPoseChanged; /// Event fires when the device bound to this action is connected or disconnected public event SteamVR_Action_Pose.DeviceConnectedChangeHandler onDeviceConnectedChanged; /// True when the orientation of the pose has changhed more than changeTolerance in the last update. Note: Will only return true if the action is also active. public override bool changed { get; protected set; } /// The value of the action's 'changed' during the previous update public override bool lastChanged { get; protected set; } /// The handle to the origin of the component that was used to update this pose public override ulong activeOrigin { get { if (active) return poseActionData.activeOrigin; return 0; } } /// The handle to the origin of the component that was used to update the value for this action (for the previous update) public override ulong lastActiveOrigin { get { return lastPoseActionData.activeOrigin; } } /// True if this action is bound and the ActionSet is active public override bool active { get { return activeBinding && action.actionSet.IsActive(inputSource); } } /// True if the action is bound public override bool activeBinding { get { return poseActionData.bActive; } } /// If the action was active (ActionSet active and binding active) during the last update public override bool lastActive { get; protected set; } /// If the action's binding was active during the previous update public override bool lastActiveBinding { get { return lastPoseActionData.bActive; } } /// The state of the tracking system that is used to create pose data (position, rotation, etc) public ETrackingResult trackingState { get { return poseActionData.pose.eTrackingResult; } } /// The tracking state for this pose during the previous update public ETrackingResult lastTrackingState { get { return lastPoseActionData.pose.eTrackingResult; } } /// True if the pose retrieved for this action and input source is valid (good data from the tracking source) public bool poseIsValid { get { return poseActionData.pose.bPoseIsValid; } } /// True if the pose was valid during the previous update public bool lastPoseIsValid { get { return lastPoseActionData.pose.bPoseIsValid; } } /// True if the device bound to this action and input source is connected public bool deviceIsConnected { get { return poseActionData.pose.bDeviceIsConnected; } } /// True if the device bound to this action was connected during the previous update public bool lastDeviceIsConnected { get { return lastPoseActionData.pose.bDeviceIsConnected; } } /// The local position of this action relative to the universe origin public Vector3 localPosition { get; protected set; } /// The local rotation of this action relative to the universe origin public Quaternion localRotation { get; protected set; } /// The local position for this pose during the previous update public Vector3 lastLocalPosition { get; protected set; } /// The local rotation for this pose during the previous update public Quaternion lastLocalRotation { get; protected set; } /// The local velocity of this pose relative to the universe origin public Vector3 velocity { get; protected set; } /// The velocity for this pose during the previous update public Vector3 lastVelocity { get; protected set; } /// The local angular velocity of this pose relative to the universe origin public Vector3 angularVelocity { get; protected set; } /// The angular velocity for this pose during the previous update public Vector3 lastAngularVelocity { get; protected set; } protected InputPoseActionData_t poseActionData = new InputPoseActionData_t(); protected InputPoseActionData_t lastPoseActionData = new InputPoseActionData_t(); protected InputPoseActionData_t tempPoseActionData = new InputPoseActionData_t(); protected SteamVR_Action_Pose poseAction; /// /// [Should not be called by user code] Sets up the internals of the action source before SteamVR has been initialized. /// public override void Preinitialize(SteamVR_Action wrappingAction, SteamVR_Input_Sources forInputSource) { base.Preinitialize(wrappingAction, forInputSource); poseAction = wrappingAction as SteamVR_Action_Pose; } /// /// [Should not be called by user code] /// Initializes the handle for the inputSource, the pose action data size, and any other related SteamVR data. /// public override void Initialize() { base.Initialize(); if (poseActionData_size == 0) poseActionData_size = (uint)Marshal.SizeOf(typeof(InputPoseActionData_t)); } /// /// Removes all listeners, useful for dispose pattern /// public virtual void RemoveAllListeners() { Delegate[] delegates; if (onActiveChange != null) { delegates = onActiveChange.GetInvocationList(); if (delegates != null) foreach (Delegate existingDelegate in delegates) onActiveChange -= (SteamVR_Action_Pose.ActiveChangeHandler)existingDelegate; } if (onActiveBindingChange != null) { delegates = onActiveBindingChange.GetInvocationList(); if (delegates != null) foreach (Delegate existingDelegate in delegates) onActiveBindingChange -= (SteamVR_Action_Pose.ActiveChangeHandler)existingDelegate; } if (onChange != null) { delegates = onChange.GetInvocationList(); if (delegates != null) foreach (Delegate existingDelegate in delegates) onChange -= (SteamVR_Action_Pose.ChangeHandler)existingDelegate; } if (onUpdate != null) { delegates = onUpdate.GetInvocationList(); if (delegates != null) foreach (Delegate existingDelegate in delegates) onUpdate -= (SteamVR_Action_Pose.UpdateHandler)existingDelegate; } if (onTrackingChanged != null) { delegates = onTrackingChanged.GetInvocationList(); if (delegates != null) foreach (Delegate existingDelegate in delegates) onTrackingChanged -= (SteamVR_Action_Pose.TrackingChangeHandler)existingDelegate; } if (onValidPoseChanged != null) { delegates = onValidPoseChanged.GetInvocationList(); if (delegates != null) foreach (Delegate existingDelegate in delegates) onValidPoseChanged -= (SteamVR_Action_Pose.ValidPoseChangeHandler)existingDelegate; } if (onDeviceConnectedChanged != null) { delegates = onDeviceConnectedChanged.GetInvocationList(); if (delegates != null) foreach (Delegate existingDelegate in delegates) onDeviceConnectedChanged -= (SteamVR_Action_Pose.DeviceConnectedChangeHandler)existingDelegate; } } /// [Should not be called by user code] /// Updates the data for this action and this input source. Sends related events. /// public override void UpdateValue() { UpdateValue(false); } public static float framesAhead = -1; /// [Should not be called by user code] /// Updates the data for this action and this input source. Sends related events. /// public virtual void UpdateValue(bool skipStateAndEventUpdates) { lastChanged = changed; lastPoseActionData = poseActionData; lastLocalPosition = localPosition; lastLocalRotation = localRotation; lastVelocity = velocity; lastAngularVelocity = angularVelocity; EVRInputError err; if (framesAhead == -1) err = OpenVR.Input.GetPoseActionDataForNextFrame(handle, universeOrigin, ref poseActionData, poseActionData_size, inputSourceHandle); else err = OpenVR.Input.GetPoseActionDataRelativeToNow(handle, universeOrigin, framesAhead * (1 / SteamVR.instance.hmd_DisplayFrequency), ref poseActionData, poseActionData_size, inputSourceHandle); if (err != EVRInputError.None) { Debug.LogError("[SteamVR] GetPoseActionData error (" + fullPath + "): " + err.ToString() + " Handle: " + handle.ToString() + ". Input source: " + inputSource.ToString()); } if (active) { SetCacheVariables(); changed = GetChanged(); } if (changed) changedTime = updateTime; if (skipStateAndEventUpdates == false) CheckAndSendEvents(); } protected void SetCacheVariables() { localPosition = poseActionData.pose.mDeviceToAbsoluteTracking.GetPosition(); localRotation = poseActionData.pose.mDeviceToAbsoluteTracking.GetRotation(); velocity = GetUnityCoordinateVelocity(poseActionData.pose.vVelocity); angularVelocity = GetUnityCoordinateAngularVelocity(poseActionData.pose.vAngularVelocity); updateTime = Time.realtimeSinceStartup; } protected bool GetChanged() { if (Vector3.Distance(localPosition, lastLocalPosition) > changeTolerance) return true; else if (Mathf.Abs(Quaternion.Angle(localRotation, lastLocalRotation)) > changeTolerance) return true; else if (Vector3.Distance(velocity, lastVelocity) > changeTolerance) return true; else if (Vector3.Distance(angularVelocity, lastAngularVelocity) > changeTolerance) return true; return false; } /// /// SteamVR keeps a log of past poses so you can retrieve old poses or estimated poses in the future by passing in a secondsFromNow value that is negative or positive. /// /// The time offset in the future (estimated) or in the past (previously recorded) you want to get data from /// true if we successfully returned a pose public bool GetVelocitiesAtTimeOffset(float secondsFromNow, out Vector3 velocityAtTime, out Vector3 angularVelocityAtTime) { EVRInputError err = OpenVR.Input.GetPoseActionDataRelativeToNow(handle, universeOrigin, secondsFromNow, ref tempPoseActionData, poseActionData_size, inputSourceHandle); if (err != EVRInputError.None) { Debug.LogError("[SteamVR] GetPoseActionData error (" + fullPath + "): " + err.ToString() + " handle: " + handle.ToString()); //todo: this should be an error velocityAtTime = Vector3.zero; angularVelocityAtTime = Vector3.zero; return false; } velocityAtTime = GetUnityCoordinateVelocity(tempPoseActionData.pose.vVelocity); angularVelocityAtTime = GetUnityCoordinateAngularVelocity(tempPoseActionData.pose.vAngularVelocity); return true; } /// /// SteamVR keeps a log of past poses so you can retrieve old poses or estimated poses in the future by passing in a secondsFromNow value that is negative or positive. /// /// The time offset in the future (estimated) or in the past (previously recorded) you want to get data from /// true if we successfully returned a pose public bool GetPoseAtTimeOffset(float secondsFromNow, out Vector3 positionAtTime, out Quaternion rotationAtTime, out Vector3 velocityAtTime, out Vector3 angularVelocityAtTime) { EVRInputError err = OpenVR.Input.GetPoseActionDataRelativeToNow(handle, universeOrigin, secondsFromNow, ref tempPoseActionData, poseActionData_size, inputSourceHandle); if (err != EVRInputError.None) { Debug.LogError("[SteamVR] GetPoseActionData error (" + fullPath + "): " + err.ToString() + " handle: " + handle.ToString()); //todo: this should be an error velocityAtTime = Vector3.zero; angularVelocityAtTime = Vector3.zero; positionAtTime = Vector3.zero; rotationAtTime = Quaternion.identity; return false; } velocityAtTime = GetUnityCoordinateVelocity(tempPoseActionData.pose.vVelocity); angularVelocityAtTime = GetUnityCoordinateAngularVelocity(tempPoseActionData.pose.vAngularVelocity); positionAtTime = tempPoseActionData.pose.mDeviceToAbsoluteTracking.GetPosition(); rotationAtTime = tempPoseActionData.pose.mDeviceToAbsoluteTracking.GetRotation(); return true; } /// /// Update a transform's local position and local roation to match the pose. /// /// The transform of the object to be updated public void UpdateTransform(Transform transformToUpdate) { transformToUpdate.localPosition = localPosition; transformToUpdate.localRotation = localRotation; } protected virtual void CheckAndSendEvents() { if (trackingState != lastTrackingState && onTrackingChanged != null) onTrackingChanged.Invoke(poseAction, inputSource, trackingState); if (poseIsValid != lastPoseIsValid && onValidPoseChanged != null) onValidPoseChanged.Invoke(poseAction, inputSource, poseIsValid); if (deviceIsConnected != lastDeviceIsConnected && onDeviceConnectedChanged != null) onDeviceConnectedChanged.Invoke(poseAction, inputSource, deviceIsConnected); if (changed && onChange != null) onChange.Invoke(poseAction, inputSource); if (active != lastActive && onActiveChange != null) onActiveChange.Invoke(poseAction, inputSource, active); if (activeBinding != lastActiveBinding && onActiveBindingChange != null) onActiveBindingChange.Invoke(poseAction, inputSource, activeBinding); if (onUpdate != null) onUpdate.Invoke(poseAction, inputSource); } protected Vector3 GetUnityCoordinateVelocity(HmdVector3_t vector) { return GetUnityCoordinateVelocity(vector.v0, vector.v1, vector.v2); } protected Vector3 GetUnityCoordinateAngularVelocity(HmdVector3_t vector) { return GetUnityCoordinateAngularVelocity(vector.v0, vector.v1, vector.v2); } protected Vector3 GetUnityCoordinateVelocity(float x, float y, float z) { Vector3 vector = new Vector3(); vector.x = x; vector.y = y; vector.z = -z; return vector; } protected Vector3 GetUnityCoordinateAngularVelocity(float x, float y, float z) { Vector3 vector = new Vector3(); vector.x = -x; vector.y = -y; vector.z = z; return vector; } } /// /// Boolean actions are either true or false. There is an onStateUp and onStateDown event for the rising and falling edge. /// public interface ISteamVR_Action_Pose : ISteamVR_Action_In_Source { /// The local position of this action relative to the universe origin Vector3 localPosition { get; } /// The local rotation of this action relative to the universe origin Quaternion localRotation { get; } /// The state of the tracking system that is used to create pose data (position, rotation, etc) ETrackingResult trackingState { get; } /// The local velocity of this pose relative to the universe origin Vector3 velocity { get; } /// The local angular velocity of this pose relative to the universe origin Vector3 angularVelocity { get; } /// True if the pose retrieved for this action and input source is valid (good data from the tracking source) bool poseIsValid { get; } /// True if the device bound to this action and input source is connected bool deviceIsConnected { get; } /// The local position for this pose during the previous update Vector3 lastLocalPosition { get; } /// The local rotation for this pose during the previous update Quaternion lastLocalRotation { get; } /// The tracking state for this pose during the previous update ETrackingResult lastTrackingState { get; } /// The velocity for this pose during the previous update Vector3 lastVelocity { get; } /// The angular velocity for this pose during the previous update Vector3 lastAngularVelocity { get; } /// True if the pose was valid during the previous update bool lastPoseIsValid { get; } /// True if the device bound to this action was connected during the previous update bool lastDeviceIsConnected { get; } } }