SteamVR_Action_Pose.cs 45.6 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
//======= 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]
    /// <summary>
    /// 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.
    /// </summary>
    public class SteamVR_Action_Pose : SteamVR_Action_Pose_Base<SteamVR_Action_Pose_Source_Map<SteamVR_Action_Pose_Source>, 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);

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> Event fires when the active state (ActionSet active and binding active) changes</summary>
        public event ActiveChangeHandler onActiveChange
        { add { sourceMap[SteamVR_Input_Sources.Any].onActiveChange += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onActiveChange -= value; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> Event fires when the active state of the binding changes</summary>
        public event ActiveChangeHandler onActiveBindingChange
        { add { sourceMap[SteamVR_Input_Sources.Any].onActiveBindingChange += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onActiveBindingChange -= value; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> Event fires when the orientation of the pose changes more than the changeTolerance</summary>
        public event ChangeHandler onChange
        { add { sourceMap[SteamVR_Input_Sources.Any].onChange += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onChange -= value; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> Event fires when the action is updated</summary>
        public event UpdateHandler onUpdate
        { add { sourceMap[SteamVR_Input_Sources.Any].onUpdate += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onUpdate -= value; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> Event fires when the state of the tracking has changed</summary>
        public event TrackingChangeHandler onTrackingChanged
        { add { sourceMap[SteamVR_Input_Sources.Any].onTrackingChanged += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onTrackingChanged -= value; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> Event fires when the validity of the pose has changed</summary>
        public event ValidPoseChangeHandler onValidPoseChanged
        { add { sourceMap[SteamVR_Input_Sources.Any].onValidPoseChanged += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onValidPoseChanged -= value; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> Event fires when the device bound to this pose is connected or disconnected</summary>
        public event DeviceConnectedChangeHandler onDeviceConnectedChanged
        { add { sourceMap[SteamVR_Input_Sources.Any].onDeviceConnectedChanged += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onDeviceConnectedChanged -= value; } }

        /// <summary>Fires an event when a device is connected or disconnected.</summary>
        /// <param name="inputSource">The device you would like to add an event to. Any if the action is not device specific.</param>
        /// <param name="functionToCall">The method you would like to be called when a device is connected. Should take a SteamVR_Action_Pose as a param</param>
        public void AddOnDeviceConnectedChanged(SteamVR_Input_Sources inputSource, DeviceConnectedChangeHandler functionToCall)
        {
            sourceMap[inputSource].onDeviceConnectedChanged += functionToCall;
        }

        /// <summary>Stops executing the function setup by the corresponding AddListener</summary>
        /// <param name="inputSource">The device you would like to remove an event from. Any if the action is not device specific.</param>
        /// <param name="functionToStopCalling">The method you would like to stop calling when a device is connected. Should take a SteamVR_Action_Pose as a param</param>
        public void RemoveOnDeviceConnectedChanged(SteamVR_Input_Sources inputSource, DeviceConnectedChangeHandler functionToStopCalling)
        {
            sourceMap[inputSource].onDeviceConnectedChanged -= functionToStopCalling;
        }


        /// <summary>Fires an event when the tracking of the device has changed</summary>
        /// <param name="inputSource">The device you would like to add an event to. Any if the action is not device specific.</param>
        /// <param name="functionToCall">The method you would like to be called when tracking has changed. Should take a SteamVR_Action_Pose as a param</param>
        public void AddOnTrackingChanged(SteamVR_Input_Sources inputSource, TrackingChangeHandler functionToCall)
        {
            sourceMap[inputSource].onTrackingChanged += functionToCall;
        }

        /// <summary>Stops executing the function setup by the corresponding AddListener</summary>
        /// <param name="inputSource">The device you would like to remove an event from. Any if the action is not device specific.</param>
        /// <param name="functionToStopCalling">The method you would like to stop calling when tracking has changed. Should take a SteamVR_Action_Pose as a param</param>
        public void RemoveOnTrackingChanged(SteamVR_Input_Sources inputSource, TrackingChangeHandler functionToStopCalling)
        {
            sourceMap[inputSource].onTrackingChanged -= functionToStopCalling;
        }


        /// <summary>Fires an event when the device now has a valid pose or no longer has a valid pose</summary>
        /// <param name="inputSource">The device you would like to add an event to. Any if the action is not device specific.</param>
        /// <param name="functionToCall">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</param>
        public void AddOnValidPoseChanged(SteamVR_Input_Sources inputSource, ValidPoseChangeHandler functionToCall)
        {
            sourceMap[inputSource].onValidPoseChanged += functionToCall;
        }

        /// <summary>Stops executing the function setup by the corresponding AddListener</summary>
        /// <param name="inputSource">The device you would like to remove an event from. Any if the action is not device specific.</param>
        /// <param name="functionToStopCalling">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</param>
        public void RemoveOnValidPoseChanged(SteamVR_Input_Sources inputSource, ValidPoseChangeHandler functionToStopCalling)
        {
            sourceMap[inputSource].onValidPoseChanged -= functionToStopCalling;
        }


        /// <summary>Executes a function when this action's bound state changes</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public void AddOnActiveChangeListener(SteamVR_Input_Sources inputSource, ActiveChangeHandler functionToCall)
        {
            sourceMap[inputSource].onActiveChange += functionToCall;
        }

        /// <summary>Stops executing the function setup by the corresponding AddListener</summary>
        /// <param name="functionToStopCalling">The local function that you've setup to receive update events</param>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public void RemoveOnActiveChangeListener(SteamVR_Input_Sources inputSource, ActiveChangeHandler functionToStopCalling)
        {
            sourceMap[inputSource].onActiveChange -= functionToStopCalling;
        }

        /// <summary>Executes a function when the state of this action (with the specified inputSource) changes</summary>
        /// <param name="functionToCall">A local function that receives the boolean action who's state has changed, the corresponding input source, and the new value</param>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public void AddOnChangeListener(SteamVR_Input_Sources inputSource, ChangeHandler functionToCall)
        {
            sourceMap[inputSource].onChange += functionToCall;
        }

        /// <summary>Stops executing the function setup by the corresponding AddListener</summary>
        /// <param name="functionToStopCalling">The local function that you've setup to receive on change events</param>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public void RemoveOnChangeListener(SteamVR_Input_Sources inputSource, ChangeHandler functionToStopCalling)
        {
            sourceMap[inputSource].onChange -= functionToStopCalling;
        }

        /// <summary>Executes a function when the state of this action (with the specified inputSource) is updated.</summary>
        /// <param name="functionToCall">A local function that receives the boolean action who's state has changed, the corresponding input source, and the new value</param>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public void AddOnUpdateListener(SteamVR_Input_Sources inputSource, UpdateHandler functionToCall)
        {
            sourceMap[inputSource].onUpdate += functionToCall;
        }

        /// <summary>Stops executing the function setup by the corresponding AddListener</summary>
        /// <param name="functionToStopCalling">The local function that you've setup to receive update events</param>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public void RemoveOnUpdateListener(SteamVR_Input_Sources inputSource, UpdateHandler functionToStopCalling)
        {
            sourceMap[inputSource].onUpdate -= functionToStopCalling;
        }

        /// <summary>
        /// Removes all listeners, useful for dispose pattern
        /// </summary>
        public void RemoveAllListeners(SteamVR_Input_Sources input_Sources)
        {
            sourceMap[input_Sources].RemoveAllListeners();
        }

        void ISerializationCallbackReceiver.OnBeforeSerialize() { }

        void ISerializationCallbackReceiver.OnAfterDeserialize()
        {
            InitAfterDeserialize();
        }

        /// <summary>
        /// Sets all pose and skeleton actions to use the specified universe origin.
        /// </summary>
        public static void SetTrackingUniverseOrigin(ETrackingUniverseOrigin newOrigin)
        {
            SetUniverseOrigin(newOrigin);
            OpenVR.Compositor.SetTrackingSpace(newOrigin);
        }
    }

    [Serializable]
    /// <summary>
    /// The base pose action (pose and skeleton inherit from this)
    /// </summary>
    public abstract class SteamVR_Action_Pose_Base<SourceMap, SourceElement> : SteamVR_Action_In<SourceMap, SourceElement>, ISteamVR_Action_Pose
        where SourceMap : SteamVR_Action_Pose_Source_Map<SourceElement>, new()
        where SourceElement : SteamVR_Action_Pose_Source, new()
    {
        /// <summary>
        /// Sets all pose (and skeleton) actions to use the specified universe origin.
        /// </summary>
        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);
            }
        }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> The local position of this action relative to the universe origin</summary>
        public Vector3 localPosition { get { return sourceMap[SteamVR_Input_Sources.Any].localPosition; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> The local rotation of this action relative to the universe origin</summary>
        public Quaternion localRotation { get { return sourceMap[SteamVR_Input_Sources.Any].localRotation; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> The state of the tracking system that is used to create pose data (position, rotation, etc)</summary>
        public ETrackingResult trackingState { get { return sourceMap[SteamVR_Input_Sources.Any].trackingState; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> The local velocity of this pose relative to the universe origin</summary>
        public Vector3 velocity { get { return sourceMap[SteamVR_Input_Sources.Any].velocity; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> The local angular velocity of this pose relative to the universe origin</summary>
        public Vector3 angularVelocity { get { return sourceMap[SteamVR_Input_Sources.Any].angularVelocity; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> True if the pose retrieved for this action and input source is valid (good data from the tracking source)</summary>
        public bool poseIsValid { get { return sourceMap[SteamVR_Input_Sources.Any].poseIsValid; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> True if the device bound to this action and input source is connected</summary>
        public bool deviceIsConnected { get { return sourceMap[SteamVR_Input_Sources.Any].deviceIsConnected; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> The local position for this pose during the previous update</summary>
        public Vector3 lastLocalPosition { get { return sourceMap[SteamVR_Input_Sources.Any].lastLocalPosition; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> The local rotation for this pose during the previous update</summary>
        public Quaternion lastLocalRotation { get { return sourceMap[SteamVR_Input_Sources.Any].lastLocalRotation; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> The tracking state for this pose during the previous update</summary>
        public ETrackingResult lastTrackingState { get { return sourceMap[SteamVR_Input_Sources.Any].lastTrackingState; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> The velocity for this pose during the previous update</summary>
        public Vector3 lastVelocity { get { return sourceMap[SteamVR_Input_Sources.Any].lastVelocity; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> The angular velocity for this pose during the previous update</summary>
        public Vector3 lastAngularVelocity { get { return sourceMap[SteamVR_Input_Sources.Any].lastAngularVelocity; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> True if the pose was valid during the previous update</summary>
        public bool lastPoseIsValid { get { return sourceMap[SteamVR_Input_Sources.Any].lastPoseIsValid; } }

        /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> True if the device bound to this action was connected during the previous update</summary>
        public bool lastDeviceIsConnected { get { return sourceMap[SteamVR_Input_Sources.Any].lastDeviceIsConnected; } }


        public SteamVR_Action_Pose_Base() { }

        /// <summary>
        /// <strong>[Should not be called by user code]</strong>
        /// Updates the data for all the input sources the system has detected need to be updated.
        /// </summary>
        public virtual void UpdateValues(bool skipStateAndEventUpdates)
        {
            sourceMap.UpdateValues(skipStateAndEventUpdates);
        }

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        /// <param name="secondsFromNow">The time offset in the future (estimated) or in the past (previously recorded) you want to get data from</param>
        /// <returns>true if the call succeeded</returns>
        public bool GetVelocitiesAtTimeOffset(SteamVR_Input_Sources inputSource, float secondsFromNow, out Vector3 velocity, out Vector3 angularVelocity)
        {
            return sourceMap[inputSource].GetVelocitiesAtTimeOffset(secondsFromNow, out velocity, out angularVelocity);
        }

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        /// <param name="secondsFromNow">The time offset in the future (estimated) or in the past (previously recorded) you want to get data from</param>
        /// <returns>true if the call succeeded</returns>
        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);
        }

        /// <summary>
        /// Update a transform's local position and local roation to match the pose from the most recent update
        /// </summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        /// <param name="transformToUpdate">The transform of the object to be updated</param>
        public virtual void UpdateTransform(SteamVR_Input_Sources inputSource, Transform transformToUpdate)
        {
            sourceMap[inputSource].UpdateTransform(transformToUpdate);
        }

        /// <summary>The local position of this action relative to the universe origin</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public Vector3 GetLocalPosition(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].localPosition;
        }

        /// <summary>The local rotation of this action relative to the universe origin</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public Quaternion GetLocalRotation(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].localRotation;
        }

        /// <summary>The local velocity of this pose relative to the universe origin</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public Vector3 GetVelocity(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].velocity;
        }

        /// <summary>The local angular velocity of this pose relative to the universe origin</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public Vector3 GetAngularVelocity(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].angularVelocity;
        }

        /// <summary>True if the device bound to this action and input source is connected</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public bool GetDeviceIsConnected(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].deviceIsConnected;
        }

        /// <summary>True if the pose retrieved for this action and input source is valid (good data from the tracking source)</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public bool GetPoseIsValid(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].poseIsValid;
        }

        /// <summary>The state of the tracking system that is used to create pose data (position, rotation, etc)</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public ETrackingResult GetTrackingResult(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].trackingState;
        }



        /// <summary>The local position for this pose during the previous update</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public Vector3 GetLastLocalPosition(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].lastLocalPosition;
        }

        /// <summary>The local rotation for this pose during the previous update</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public Quaternion GetLastLocalRotation(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].lastLocalRotation;
        }

        /// <summary>The velocity for this pose during the previous update</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public Vector3 GetLastVelocity(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].lastVelocity;
        }

        /// <summary>The angular velocity for this pose during the previous update</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public Vector3 GetLastAngularVelocity(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].lastAngularVelocity;
        }

        /// <summary>True if the device bound to this action was connected during the previous update</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public bool GetLastDeviceIsConnected(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].lastDeviceIsConnected;
        }

        /// <summary>True if the pose was valid during the previous update</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public bool GetLastPoseIsValid(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].lastPoseIsValid;
        }

        /// <summary>The tracking state for this pose during the previous update</summary>
        /// <param name="inputSource">The device you would like to get data from. Any if the action is not device specific.</param>
        public ETrackingResult GetLastTrackingResult(SteamVR_Input_Sources inputSource)
        {
            return sourceMap[inputSource].lastTrackingState;
        }
    }

    /// <summary>
    /// Boolean actions are either true or false. There is an onStateUp and onStateDown event for the rising and falling edge.
    /// </summary>
    public class SteamVR_Action_Pose_Source_Map<Source> : SteamVR_Action_In_Source_Map<Source>
        where Source : SteamVR_Action_Pose_Source, new()
    {
        /// <summary>
        /// Sets all pose (and skeleton) actions to use the specified universe origin without going through the sourcemap indexer
        /// </summary>
        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;

        /// <summary>The distance the pose needs to move/rotate before a change is detected</summary>
        public float changeTolerance = Mathf.Epsilon;

        /// <summary>Event fires when the active state (ActionSet active and binding active) changes</summary>
        public event SteamVR_Action_Pose.ActiveChangeHandler onActiveChange;

        /// <summary>Event fires when the active state of the binding changes</summary>
        public event SteamVR_Action_Pose.ActiveChangeHandler onActiveBindingChange;

        /// <summary>Event fires when the orientation of the pose changes more than the changeTolerance</summary>
        public event SteamVR_Action_Pose.ChangeHandler onChange;

        /// <summary>Event fires when the action is updated</summary>
        public event SteamVR_Action_Pose.UpdateHandler onUpdate;

        /// <summary>Event fires when the state of the tracking system that is used to create pose data (position, rotation, etc) changes</summary>
        public event SteamVR_Action_Pose.TrackingChangeHandler onTrackingChanged;

        /// <summary>Event fires when the state of the pose data retrieved for this action changes validity (good/bad data from the tracking source)</summary>
        public event SteamVR_Action_Pose.ValidPoseChangeHandler onValidPoseChanged;

        /// <summary>Event fires when the device bound to this action is connected or disconnected</summary>
        public event SteamVR_Action_Pose.DeviceConnectedChangeHandler onDeviceConnectedChanged;



        /// <summary>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.</summary>
        public override bool changed { get; protected set; }

        /// <summary>The value of the action's 'changed' during the previous update</summary>
        public override bool lastChanged { get; protected set; }

        /// <summary>The handle to the origin of the component that was used to update this pose</summary>
        public override ulong activeOrigin
        {
            get
            {
                if (active)
                    return poseActionData.activeOrigin;

                return 0;
            }
        }

        /// <summary>The handle to the origin of the component that was used to update the value for this action (for the previous update)</summary>
        public override ulong lastActiveOrigin { get { return lastPoseActionData.activeOrigin; } }

        /// <summary>True if this action is bound and the ActionSet is active</summary>
        public override bool active { get { return activeBinding && action.actionSet.IsActive(inputSource); } }

        /// <summary>True if the action is bound</summary>
        public override bool activeBinding { get { return poseActionData.bActive; } }


        /// <summary>If the action was active (ActionSet active and binding active) during the last update</summary>
        public override bool lastActive { get; protected set; }

        /// <summary>If the action's binding was active during the previous update</summary>
        public override bool lastActiveBinding { get { return lastPoseActionData.bActive; } }

        /// <summary>The state of the tracking system that is used to create pose data (position, rotation, etc)</summary>
        public ETrackingResult trackingState { get { return poseActionData.pose.eTrackingResult; } }

        /// <summary>The tracking state for this pose during the previous update</summary>
        public ETrackingResult lastTrackingState { get { return lastPoseActionData.pose.eTrackingResult; } }

        /// <summary>True if the pose retrieved for this action and input source is valid (good data from the tracking source)</summary>
        public bool poseIsValid { get { return poseActionData.pose.bPoseIsValid; } }

        /// <summary>True if the pose was valid during the previous update</summary>
        public bool lastPoseIsValid { get { return lastPoseActionData.pose.bPoseIsValid; } }

        /// <summary>True if the device bound to this action and input source is connected</summary>
        public bool deviceIsConnected { get { return poseActionData.pose.bDeviceIsConnected; } }

        /// <summary>True if the device bound to this action was connected during the previous update</summary>
        public bool lastDeviceIsConnected { get { return lastPoseActionData.pose.bDeviceIsConnected; } }


        /// <summary>The local position of this action relative to the universe origin</summary>
        public Vector3 localPosition { get; protected set; }

        /// <summary>The local rotation of this action relative to the universe origin</summary>
        public Quaternion localRotation { get; protected set; }

        /// <summary>The local position for this pose during the previous update</summary>
        public Vector3 lastLocalPosition { get; protected set; }

        /// <summary>The local rotation for this pose during the previous update</summary>
        public Quaternion lastLocalRotation { get; protected set; }

        /// <summary>The local velocity of this pose relative to the universe origin</summary>
        public Vector3 velocity { get; protected set; }

        /// <summary>The velocity for this pose during the previous update</summary>
        public Vector3 lastVelocity { get; protected set; }

        /// <summary>The local angular velocity of this pose relative to the universe origin</summary>
        public Vector3 angularVelocity { get; protected set; }

        /// <summary>The angular velocity for this pose during the previous update</summary>
        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;

        /// <summary>
        /// <strong>[Should not be called by user code]</strong> Sets up the internals of the action source before SteamVR has been initialized.
        /// </summary>
        public override void Preinitialize(SteamVR_Action wrappingAction, SteamVR_Input_Sources forInputSource)
        {
            base.Preinitialize(wrappingAction, forInputSource);
            poseAction = wrappingAction as SteamVR_Action_Pose;
        }

        /// <summary>
        /// <strong>[Should not be called by user code]</strong>
        /// Initializes the handle for the inputSource, the pose action data size, and any other related SteamVR data.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            if (poseActionData_size == 0)
                poseActionData_size = (uint)Marshal.SizeOf(typeof(InputPoseActionData_t));
        }

        /// <summary>
        /// Removes all listeners, useful for dispose pattern
        /// </summary>
        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 (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;
            }
        }

        /// <summary><strong>[Should not be called by user code]</strong>
        /// Updates the data for this action and this input source. Sends related events.
        /// </summary>
        public override void UpdateValue()
        {
            UpdateValue(false);
        }

        public static float framesAhead = 2;

        /// <summary><strong>[Should not be called by user code]</strong>
        /// Updates the data for this action and this input source. Sends related events.
        /// </summary>
        public virtual void UpdateValue(bool skipStateAndEventUpdates)
        {
            lastChanged = changed;
            lastPoseActionData = poseActionData;
            lastLocalPosition = localPosition;
            lastLocalRotation = localRotation;
            lastVelocity = velocity;
            lastAngularVelocity = angularVelocity;

            EVRInputError err;

            if (framesAhead == 0)
                err = OpenVR.Input.GetPoseActionDataForNextFrame(handle, universeOrigin, ref poseActionData, poseActionData_size, inputSourceHandle);
            else
                err = OpenVR.Input.GetPoseActionDataRelativeToNow(handle, universeOrigin, framesAhead * (Time.timeScale / SteamVR.instance.hmd_DisplayFrequency), ref poseActionData, poseActionData_size, inputSourceHandle);

            if (err != EVRInputError.None)
            {
                Debug.LogError("<b>[SteamVR]</b> 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;
        }

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="secondsFromNow">The time offset in the future (estimated) or in the past (previously recorded) you want to get data from</param>
        /// <returns>true if we successfully returned a pose</returns>
        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("<b>[SteamVR]</b> 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;
        }

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="secondsFromNow">The time offset in the future (estimated) or in the past (previously recorded) you want to get data from</param>
        /// <returns>true if we successfully returned a pose</returns>
        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("<b>[SteamVR]</b> 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;
        }

        /// <summary>
        /// Update a transform's local position and local roation to match the pose.
        /// </summary>
        /// <param name="transformToUpdate">The transform of the object to be updated</param>
        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;
        }
    }

    /// <summary>
    /// Boolean actions are either true or false. There is an onStateUp and onStateDown event for the rising and falling edge.
    /// </summary>
    public interface ISteamVR_Action_Pose : ISteamVR_Action_In_Source
    {
        /// <summary>The local position of this action relative to the universe origin</summary>
        Vector3 localPosition { get; }

        /// <summary>The local rotation of this action relative to the universe origin</summary>
        Quaternion localRotation { get; }

        /// <summary>The state of the tracking system that is used to create pose data (position, rotation, etc)</summary>
        ETrackingResult trackingState { get; }

        /// <summary>The local velocity of this pose relative to the universe origin</summary>
        Vector3 velocity { get; }

        /// <summary>The local angular velocity of this pose relative to the universe origin</summary>
        Vector3 angularVelocity { get; }

        /// <summary>True if the pose retrieved for this action and input source is valid (good data from the tracking source)</summary>
        bool poseIsValid { get; }

        /// <summary>True if the device bound to this action and input source is connected</summary>
        bool deviceIsConnected { get; }


        /// <summary>The local position for this pose during the previous update</summary>
        Vector3 lastLocalPosition { get; }

        /// <summary>The local rotation for this pose during the previous update</summary>
        Quaternion lastLocalRotation { get; }

        /// <summary>The tracking state for this pose during the previous update</summary>
        ETrackingResult lastTrackingState { get; }

        /// <summary>The velocity for this pose during the previous update</summary>
        Vector3 lastVelocity { get; }

        /// <summary>The angular velocity for this pose during the previous update</summary>
        Vector3 lastAngularVelocity { get; }

        /// <summary>True if the pose was valid during the previous update</summary>
        bool lastPoseIsValid { get; }

        /// <summary>True if the device bound to this action was connected during the previous update</summary>
        bool lastDeviceIsConnected { get; }
    }
}