-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathXRGrabInteractable.cs
2278 lines (1976 loc) · 101 KB
/
XRGrabInteractable.cs
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
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
using System;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.Profiling;
using UnityEngine.Serialization;
using UnityEngine.XR.Interaction.Toolkit.Transformers;
using UnityEngine.XR.Interaction.Toolkit.Utilities;
using UnityEngine.XR.Interaction.Toolkit.Utilities.Pooling;
#if BURST_PRESENT
using Unity.Burst;
#endif
////////////////////////////////////////////////////////////////////////////////////////
//
// MODITIFED to disable reparenting to root on grab
//
// modifications are marked with </HACKY MODIFICATION> ... </HACKY MODIFICATION>
//
// TODO: introduce boolean to toggle reparenting to root (instead of commenting it out...)
//
////////////////////////////////////////////////////////////////////////////////////////
namespace UnityEngine.XR.Interaction.Toolkit
{
/// <summary>
/// Interactable component that allows for basic grab functionality.
/// When this behavior is selected (grabbed) by an Interactor, this behavior will follow it around
/// and inherit velocity when released.
/// </summary>
/// <remarks>
/// <para>
/// This behavior is responsible for applying the position, rotation, and local scale calculated
/// by one or more <see cref="IXRGrabTransformer"/> implementations. A default set of grab transformers
/// are automatically added by Unity, but this functionality can be disabled to manually set those
/// used by this behavior, allowing you to customize where this component should move and rotate to.
/// </para>
/// <para>
/// Grab transformers are classified into two different types: Single and Multiple.
/// Those added to the Single Grab Transformers list are used when there is a single interactor selecting this object.
/// Those added to the Multiple Grab Transformers list are used when there are multiple interactors selecting this object.
/// You can add multiple grab transformers in a category and they will each be processed in sequence.
/// The Multiple Grab Transformers are given first opportunity to process when there are multiple grabs, and
/// the Single Grab Transformer processing will be skipped if a Multiple Grab Transformer can process in that case.
/// </para>
/// <para>
/// There are fallback rules that could allow a Single Grab Transformer to be processed when there are multiple grabs,
/// and for a Multiple Grab Transformer to be processed when there is a single grab (though a grab transformer will never be
/// processed if its <see cref="IXRGrabTransformer.canProcess"/> returns <see langword="false"/>).
/// <list type="bullet">
/// <item>
/// <description>When there is a single interactor selecting this object, the Multiple Grab Transformer will be processed only
/// if the Single Grab Transformer list is empty or if all transformers in the Single Grab Transformer list return false during processing.</description>
/// </item>
/// <item>
/// <description>When there are multiple interactors selecting this object, the Single Grab Transformer will be processed only
/// if the Multiple Grab Transformer list is empty or if all transformers in the Multiple Grab Transformer list return false during processing.</description>
/// </item>
/// </list>
/// </para>
/// </remarks>
/// <seealso cref="IXRGrabTransformer"/>
[SelectionBase]
[DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody))]
[AddComponentMenu("XR/XR Grab Interactable", 11)]
[HelpURL(XRHelpURLConstants.k_XRGrabInteractable)]
#if BURST_PRESENT
[BurstCompile]
#endif
public partial class XRGrabInteractable : XRBaseInteractable
{
const float k_DefaultTighteningAmount = 0.1f;
const float k_DefaultSmoothingAmount = 8f;
const float k_VelocityDamping = 1f;
const float k_VelocityScale = 1f;
const float k_AngularVelocityDamping = 1f;
const float k_AngularVelocityScale = 1f;
const int k_ThrowSmoothingFrameCount = 20;
const float k_DefaultAttachEaseInTime = 0.15f;
const float k_DefaultThrowSmoothingDuration = 0.25f;
const float k_DefaultThrowVelocityScale = 1.5f;
const float k_DefaultThrowAngularVelocityScale = 1f;
const float k_DeltaTimeThreshold = 0.001f;
/// <summary>
/// Controls the method used when calculating the target position of the object.
/// </summary>
/// <seealso cref="attachPointCompatibilityMode"/>
public enum AttachPointCompatibilityMode
{
/// <summary>
/// Use the default, correct method for calculating the target position of the object.
/// </summary>
Default,
/// <summary>
/// Use an additional offset from the center of mass when calculating the target position of the object.
/// Also incorporate the scale of the Interactor's Attach Transform.
/// Marked for deprecation.
/// This is the backwards compatible support mode for projects that accounted for the
/// unintended difference when using XR Interaction Toolkit prior to version <c>1.0.0-pre.4</c>.
/// To have the effective attach position be the same between all <see cref="XRBaseInteractable.MovementType"/> values, use <see cref="Default"/>.
/// </summary>
Legacy,
}
[SerializeField]
Transform m_AttachTransform;
/// <summary>
/// The attachment point Unity uses on this Interactable (will use this object's position if none set).
/// </summary>
public Transform attachTransform
{
get => m_AttachTransform;
set => m_AttachTransform = value;
}
[SerializeField]
Transform m_SecondaryAttachTransform;
/// <summary>
/// The secondary attachment point to use on this Interactable for multi-hand interaction (will use the second interactor's attach transform if none set).
/// Used for multi-grab interactions.
/// </summary>
public Transform secondaryAttachTransform
{
get => m_SecondaryAttachTransform;
set => m_SecondaryAttachTransform = value;
}
[SerializeField]
bool m_UseDynamicAttach;
/// <summary>
/// The grab pose will be based on the pose of the Interactor when the selection is made.
/// Unity will create a dynamic attachment point for each Interactor that selects this component.
/// </summary>
/// <remarks>
/// A child GameObject will be created for each Interactor that selects this component to serve as the attachment point.
/// These are cached and part of a shared pool used by all instances of <see cref="XRGrabInteractable"/>.
/// Therefore, while a reference can be obtained by calling <see cref="GetAttachTransform"/> while selected,
/// you should typically not add any components to that GameObject unless you remove them after being released
/// since it won't always be used by the same Interactable.
/// </remarks>
/// <seealso cref="attachTransform"/>
/// <seealso cref="InitializeDynamicAttachPose"/>
public bool useDynamicAttach
{
get => m_UseDynamicAttach;
set => m_UseDynamicAttach = value;
}
[SerializeField]
bool m_MatchAttachPosition = true;
/// <summary>
/// Match the position of the Interactor's attachment point when initializing the grab.
/// This will override the position of <see cref="attachTransform"/>.
/// </summary>
/// <remarks>
/// This will initialize the dynamic attachment point of this object using the position of the Interactor's attachment point.
/// This value can be overridden for a specific interactor by overriding <see cref="ShouldMatchAttachPosition"/>.
/// </remarks>
/// <seealso cref="useDynamicAttach"/>
/// <seealso cref="matchAttachRotation"/>
public bool matchAttachPosition
{
get => m_MatchAttachPosition;
set => m_MatchAttachPosition = value;
}
[SerializeField]
bool m_MatchAttachRotation = true;
/// <summary>
/// Match the rotation of the Interactor's attachment point when initializing the grab.
/// This will override the rotation of <see cref="attachTransform"/>.
/// </summary>
/// <remarks>
/// This will initialize the dynamic attachment point of this object using the rotation of the Interactor's attachment point.
/// This value can be overridden for a specific interactor by overriding <see cref="ShouldMatchAttachRotation"/>.
/// </remarks>
/// <seealso cref="useDynamicAttach"/>
/// <seealso cref="matchAttachPosition"/>
public bool matchAttachRotation
{
get => m_MatchAttachRotation;
set => m_MatchAttachRotation = value;
}
[SerializeField]
bool m_SnapToColliderVolume = true;
/// <summary>
/// Adjust the dynamic attachment point to keep it on or inside the Colliders that make up this object.
/// </summary>
/// <seealso cref="useDynamicAttach"/>
/// <seealso cref="ShouldSnapToColliderVolume"/>
/// <seealso cref="Collider.ClosestPoint"/>
public bool snapToColliderVolume
{
get => m_SnapToColliderVolume;
set => m_SnapToColliderVolume = value;
}
[SerializeField]
bool m_ReinitializeDynamicAttachEverySingleGrab = true;
/// <summary>
/// Re-initialize the dynamic attachment pose when changing from multiple grabs back to a single grab.
/// Use this if you want to keep the current pose of the object after releasing a second hand
/// rather than reverting back to the attach pose from the original grab.
/// </summary>
/// <remarks>
/// <see cref="IXRSelectInteractable.selectMode"/> must be set to <see cref="InteractableSelectMode.Multiple"/> for
/// this setting to take effect.
/// </remarks>
/// <seealso cref="useDynamicAttach"/>
/// <seealso cref="IXRSelectInteractable.selectMode"/>
public bool reinitializeDynamicAttachEverySingleGrab
{
get => m_ReinitializeDynamicAttachEverySingleGrab;
set => m_ReinitializeDynamicAttachEverySingleGrab = value;
}
[SerializeField]
float m_AttachEaseInTime = k_DefaultAttachEaseInTime;
/// <summary>
/// Time in seconds Unity eases in the attach when selected (a value of 0 indicates no easing).
/// </summary>
public float attachEaseInTime
{
get => m_AttachEaseInTime;
set => m_AttachEaseInTime = value;
}
[SerializeField]
MovementType m_MovementType = MovementType.Instantaneous;
/// <summary>
/// Specifies how this object moves when selected, either through setting the velocity of the <see cref="Rigidbody"/>,
/// moving the kinematic <see cref="Rigidbody"/> during Fixed Update, or by directly updating the <see cref="Transform"/> each frame.
/// </summary>
/// <seealso cref="XRBaseInteractable.MovementType"/>
public MovementType movementType
{
get => m_MovementType;
set
{
m_MovementType = value;
if (isSelected)
{
SetupRigidbodyDrop(m_Rigidbody);
UpdateCurrentMovementType();
SetupRigidbodyGrab(m_Rigidbody);
}
}
}
[SerializeField, Range(0f, 1f)]
float m_VelocityDamping = k_VelocityDamping;
/// <summary>
/// Scale factor of how much to dampen the existing velocity when tracking the position of the Interactor.
/// The smaller the value, the longer it takes for the velocity to decay.
/// </summary>
/// <remarks>
/// Only applies when in <see cref="XRBaseInteractable.MovementType.VelocityTracking"/> mode.
/// </remarks>
/// <seealso cref="XRBaseInteractable.MovementType.VelocityTracking"/>
/// <seealso cref="trackPosition"/>
public float velocityDamping
{
get => m_VelocityDamping;
set => m_VelocityDamping = value;
}
[SerializeField]
float m_VelocityScale = k_VelocityScale;
/// <summary>
/// Scale factor Unity applies to the tracked velocity while updating the <see cref="Rigidbody"/>
/// when tracking the position of the Interactor.
/// </summary>
/// <remarks>
/// Only applies when in <see cref="XRBaseInteractable.MovementType.VelocityTracking"/> mode.
/// </remarks>
/// <seealso cref="XRBaseInteractable.MovementType.VelocityTracking"/>
/// <seealso cref="trackPosition"/>
public float velocityScale
{
get => m_VelocityScale;
set => m_VelocityScale = value;
}
[SerializeField, Range(0f, 1f)]
float m_AngularVelocityDamping = k_AngularVelocityDamping;
/// <summary>
/// Scale factor of how much Unity dampens the existing angular velocity when tracking the rotation of the Interactor.
/// The smaller the value, the longer it takes for the angular velocity to decay.
/// </summary>
/// <remarks>
/// Only applies when in <see cref="XRBaseInteractable.MovementType.VelocityTracking"/> mode.
/// </remarks>
/// <seealso cref="XRBaseInteractable.MovementType.VelocityTracking"/>
/// <seealso cref="trackRotation"/>
public float angularVelocityDamping
{
get => m_AngularVelocityDamping;
set => m_AngularVelocityDamping = value;
}
[SerializeField]
float m_AngularVelocityScale = k_AngularVelocityScale;
/// <summary>
/// Scale factor Unity applies to the tracked angular velocity while updating the <see cref="Rigidbody"/>
/// when tracking the rotation of the Interactor.
/// </summary>
/// <remarks>
/// Only applies when in <see cref="XRBaseInteractable.MovementType.VelocityTracking"/> mode.
/// </remarks>
/// <seealso cref="XRBaseInteractable.MovementType.VelocityTracking"/>
/// <seealso cref="trackRotation"/>
public float angularVelocityScale
{
get => m_AngularVelocityScale;
set => m_AngularVelocityScale = value;
}
[SerializeField]
bool m_TrackPosition = true;
/// <summary>
/// Whether this object should follow the position of the Interactor when selected.
/// </summary>
/// <seealso cref="trackRotation"/>
/// <seealso cref="trackScale"/>
public bool trackPosition
{
get => m_TrackPosition;
set => m_TrackPosition = value;
}
[SerializeField]
bool m_SmoothPosition;
/// <summary>
/// Whether Unity applies smoothing while following the position of the Interactor when selected.
/// </summary>
/// <seealso cref="smoothPositionAmount"/>
/// <seealso cref="tightenPosition"/>
public bool smoothPosition
{
get => m_SmoothPosition;
set => m_SmoothPosition = value;
}
[SerializeField, Range(0f, 20f)]
float m_SmoothPositionAmount = k_DefaultSmoothingAmount;
/// <summary>
/// Scale factor for how much smoothing is applied while following the position of the Interactor when selected.
/// The larger the value, the closer this object will remain to the position of the Interactor.
/// </summary>
/// <seealso cref="smoothPosition"/>
/// <seealso cref="tightenPosition"/>
public float smoothPositionAmount
{
get => m_SmoothPositionAmount;
set => m_SmoothPositionAmount = value;
}
[SerializeField, Range(0f, 1f)]
float m_TightenPosition = k_DefaultTighteningAmount;
/// <summary>
/// Reduces the maximum follow position difference when using smoothing.
/// </summary>
/// <remarks>
/// Fractional amount of how close the smoothed position should remain to the position of the Interactor when using smoothing.
/// The value ranges from 0 meaning no bias in the smoothed follow distance,
/// to 1 meaning effectively no smoothing at all.
/// </remarks>
/// <seealso cref="smoothPosition"/>
/// <seealso cref="smoothPositionAmount"/>
public float tightenPosition
{
get => m_TightenPosition;
set => m_TightenPosition = value;
}
[SerializeField]
bool m_TrackRotation = true;
/// <summary>
/// Whether this object should follow the rotation of the Interactor when selected.
/// </summary>
/// <seealso cref="trackPosition"/>
/// <seealso cref="trackScale"/>
public bool trackRotation
{
get => m_TrackRotation;
set => m_TrackRotation = value;
}
[SerializeField]
bool m_SmoothRotation;
/// <summary>
/// Apply smoothing while following the rotation of the Interactor when selected.
/// </summary>
/// <seealso cref="smoothRotationAmount"/>
/// <seealso cref="tightenRotation"/>
public bool smoothRotation
{
get => m_SmoothRotation;
set => m_SmoothRotation = value;
}
[SerializeField, Range(0f, 20f)]
float m_SmoothRotationAmount = k_DefaultSmoothingAmount;
/// <summary>
/// Scale factor for how much smoothing is applied while following the rotation of the Interactor when selected.
/// The larger the value, the closer this object will remain to the rotation of the Interactor.
/// </summary>
/// <seealso cref="smoothRotation"/>
/// <seealso cref="tightenRotation"/>
public float smoothRotationAmount
{
get => m_SmoothRotationAmount;
set => m_SmoothRotationAmount = value;
}
[SerializeField, Range(0f, 1f)]
float m_TightenRotation = k_DefaultTighteningAmount;
/// <summary>
/// Reduces the maximum follow rotation difference when using smoothing.
/// </summary>
/// <remarks>
/// Fractional amount of how close the smoothed rotation should remain to the rotation of the Interactor when using smoothing.
/// The value ranges from 0 meaning no bias in the smoothed follow rotation,
/// to 1 meaning effectively no smoothing at all.
/// </remarks>
/// <seealso cref="smoothRotation"/>
/// <seealso cref="smoothRotationAmount"/>
public float tightenRotation
{
get => m_TightenRotation;
set => m_TightenRotation = value;
}
[SerializeField]
bool m_TrackScale = true;
/// <summary>
/// Whether or not the interactor will affect the scale of the object when selected.
/// </summary>
/// <seealso cref="trackPosition"/>
/// <seealso cref="trackRotation"/>
public bool trackScale
{
get => m_TrackScale;
set => m_TrackScale = value;
}
[SerializeField]
bool m_SmoothScale;
/// <summary>
/// Whether Unity applies smoothing while following the scale of the Interactor when selected.
/// </summary>
/// <seealso cref="smoothScaleAmount"/>
/// <seealso cref="tightenScale"/>
public bool smoothScale
{
get => m_SmoothScale;
set => m_SmoothScale = value;
}
[SerializeField, Range(0f, 20f)]
float m_SmoothScaleAmount = k_DefaultSmoothingAmount;
/// <summary>
/// Scale factor for how much smoothing is applied while following the scale of the Interactor when selected.
/// The larger the value, the closer this object will remain to the scale of the Interactor.
/// </summary>
/// <seealso cref="smoothScale"/>
/// <seealso cref="tightenScale"/>
public float smoothScaleAmount
{
get => m_SmoothScaleAmount;
set => m_SmoothScaleAmount = value;
}
[SerializeField, Range(0f, 1f)]
float m_TightenScale = k_DefaultTighteningAmount;
/// <summary>
/// Reduces the maximum follow scale difference when using smoothing.
/// </summary>
/// <remarks>
/// Scale factor for how much smoothing is applied while following the scale of the determined by the transformer when selected. The larger the value, the closer this object will remain to the target scale determined by the interactable's transformer.
/// The value ranges from 0 meaning no bias in the smoothed follow distance,
/// to 1 meaning effectively no smoothing at all.
/// </remarks>
/// <seealso cref="smoothScale"/>
/// <seealso cref="smoothScaleAmount"/>
public float tightenScale
{
get => m_TightenScale;
set => m_TightenScale = value;
}
[SerializeField]
bool m_ThrowOnDetach = true;
/// <summary>
/// Whether this object inherits the velocity of the Interactor when released.
/// </summary>
public bool throwOnDetach
{
get => m_ThrowOnDetach;
set => m_ThrowOnDetach = value;
}
[SerializeField]
float m_ThrowSmoothingDuration = k_DefaultThrowSmoothingDuration;
/// <summary>
/// This value represents the time over which collected samples are used for velocity calculation
/// (up to a max of 20 previous frames, which is dependent on both Smoothing Duration and framerate).
/// </summary>
/// <remarks>
/// As an example, if this value is set to 0.25, position and velocity values will be averaged over the past 0.25 seconds.
/// Each of those values is weighted (multiplied) by the <see cref="throwSmoothingCurve"/> as well.</remarks>
/// <seealso cref="throwSmoothingCurve"/>
/// <seealso cref="throwOnDetach"/>
public float throwSmoothingDuration
{
get => m_ThrowSmoothingDuration;
set => m_ThrowSmoothingDuration = value;
}
[SerializeField]
AnimationCurve m_ThrowSmoothingCurve = AnimationCurve.Linear(1f, 1f, 1f, 0f);
/// <summary>
/// The curve used to weight velocity smoothing upon throwing (most recent frames to the right).
/// </summary>
/// <remarks>
/// By default this curve is flat with a 1.0 value so all smoothing values are treated equally across the smoothing duration.
/// </remarks>
/// <seealso cref="throwSmoothingDuration"/>
/// <seealso cref="throwOnDetach"/>
public AnimationCurve throwSmoothingCurve
{
get => m_ThrowSmoothingCurve;
set => m_ThrowSmoothingCurve = value;
}
[SerializeField]
float m_ThrowVelocityScale = k_DefaultThrowVelocityScale;
/// <summary>
/// Scale factor Unity applies to this object's velocity inherited from the Interactor when released.
/// </summary>
/// <seealso cref="throwOnDetach"/>
public float throwVelocityScale
{
get => m_ThrowVelocityScale;
set => m_ThrowVelocityScale = value;
}
[SerializeField]
float m_ThrowAngularVelocityScale = k_DefaultThrowAngularVelocityScale;
/// <summary>
/// Scale factor Unity applies to this object's angular velocity inherited from the Interactor when released.
/// </summary>
/// <seealso cref="throwOnDetach"/>
public float throwAngularVelocityScale
{
get => m_ThrowAngularVelocityScale;
set => m_ThrowAngularVelocityScale = value;
}
[SerializeField, FormerlySerializedAs("m_GravityOnDetach")]
bool m_ForceGravityOnDetach;
/// <summary>
/// Forces this object to have gravity when released
/// (will still use pre-grab value if this is <see langword="false"/>).
/// </summary>
public bool forceGravityOnDetach
{
get => m_ForceGravityOnDetach;
set => m_ForceGravityOnDetach = value;
}
[SerializeField]
bool m_RetainTransformParent = true;
/// <summary>
/// Whether Unity sets the parent of this object back to its original parent this object was a child of after this object is dropped.
/// </summary>
public bool retainTransformParent
{
get => m_RetainTransformParent;
set => m_RetainTransformParent = value;
}
[SerializeField]
AttachPointCompatibilityMode m_AttachPointCompatibilityMode = AttachPointCompatibilityMode.Default;
/// <summary>
/// Controls the method used when calculating the target position of the object.
/// Use <see cref="AttachPointCompatibilityMode.Default"/> for consistent attach points
/// between all <see cref="XRBaseInteractable.MovementType"/> values.
/// Marked for deprecation, this property will be removed in a future version.
/// </summary>
/// <remarks>
/// This is a backwards compatibility option in order to keep the old, incorrect method
/// of calculating the attach point. Projects that already accounted for the difference
/// can use the Legacy option to maintain the same attach positioning from older versions
/// without needing to modify the Attach Transform position.
/// </remarks>
/// <seealso cref="AttachPointCompatibilityMode"/>
public AttachPointCompatibilityMode attachPointCompatibilityMode
{
get => m_AttachPointCompatibilityMode;
set => m_AttachPointCompatibilityMode = value;
}
[SerializeField]
List<XRBaseGrabTransformer> m_StartingSingleGrabTransformers = new List<XRBaseGrabTransformer>();
/// <summary>
/// The grab transformers that this Interactable automatically links at startup (optional, may be empty).
/// These are used when there is a single interactor selecting this object.
/// </summary>
/// <remarks>
/// To modify the grab transformers used after startup,
/// the <see cref="AddSingleGrabTransformer"/> or <see cref="RemoveSingleGrabTransformer"/> methods should be used instead.
/// </remarks>
/// <seealso cref="startingMultipleGrabTransformers"/>
public List<XRBaseGrabTransformer> startingSingleGrabTransformers
{
get => m_StartingSingleGrabTransformers;
set => m_StartingSingleGrabTransformers = value;
}
[SerializeField]
List<XRBaseGrabTransformer> m_StartingMultipleGrabTransformers = new List<XRBaseGrabTransformer>();
/// <summary>
/// The grab transformers that this Interactable automatically links at startup (optional, may be empty).
/// These are used when there are multiple interactors selecting this object.
/// </summary>
/// <remarks>
/// To modify the grab transformers used after startup,
/// the <see cref="AddMultipleGrabTransformer"/> or <see cref="RemoveMultipleGrabTransformer"/> methods should be used instead.
/// </remarks>
/// <seealso cref="startingSingleGrabTransformers"/>
public List<XRBaseGrabTransformer> startingMultipleGrabTransformers
{
get => m_StartingMultipleGrabTransformers;
set => m_StartingMultipleGrabTransformers = value;
}
[SerializeField]
bool m_AddDefaultGrabTransformers = true;
/// <summary>
/// Whether Unity will add the default set of grab transformers if either the Single or Multiple Grab Transformers lists are empty.
/// </summary>
/// <remarks>
/// Set this to <see langword="false"/> if you want to manually set the grab transformers used by populating
/// <see cref="startingSingleGrabTransformers"/> and <see cref="startingMultipleGrabTransformers"/>.
/// </remarks>
/// <seealso cref="AddDefaultSingleGrabTransformer"/>
/// <seealso cref="AddDefaultMultipleGrabTransformer"/>
public bool addDefaultGrabTransformers
{
get => m_AddDefaultGrabTransformers;
set => m_AddDefaultGrabTransformers = value;
}
/// <summary>
/// The number of single grab transformers.
/// These are the grab transformers used when there is a single interactor selecting this object.
/// </summary>
/// <seealso cref="AddSingleGrabTransformer"/>
public int singleGrabTransformersCount => m_SingleGrabTransformers.flushedCount;
/// <summary>
/// The number of multiple grab transformers.
/// These are the grab transformers used when there are multiple interactors selecting this object.
/// </summary>
/// <seealso cref="AddMultipleGrabTransformer"/>
public int multipleGrabTransformersCount => m_MultipleGrabTransformers.flushedCount;
readonly SmallRegistrationList<IXRGrabTransformer> m_SingleGrabTransformers = new SmallRegistrationList<IXRGrabTransformer>();
readonly SmallRegistrationList<IXRGrabTransformer> m_MultipleGrabTransformers = new SmallRegistrationList<IXRGrabTransformer>();
List<IXRGrabTransformer> m_GrabTransformersAddedWhenGrabbed;
bool m_GrabCountChanged;
(int, int) m_GrabCountBeforeAndAfterChange;
bool m_IsProcessingGrabTransformers;
/// <summary>
/// The number of registered grab transformers that implement <see cref="IXRDropTransformer"/>.
/// </summary>
int m_DropTransformersCount;
static readonly LinkedPool<DropEventArgs> s_DropEventArgs = new LinkedPool<DropEventArgs>(() => new DropEventArgs(), collectionCheck: false);
// World pose we are moving towards each frame (eventually will be at Interactor's attach point assuming default single grab algorithm)
Pose m_TargetPose;
Vector3 m_TargetLocalScale;
bool m_IsTargetPoseDirty;
bool m_IsTargetLocalScaleDirty;
bool isTransformDirty
{
get => m_IsTargetPoseDirty || m_IsTargetLocalScaleDirty;
set
{
m_IsTargetPoseDirty = value;
m_IsTargetLocalScaleDirty = value;
}
}
float m_CurrentAttachEaseTime;
MovementType m_CurrentMovementType;
bool m_DetachInLateUpdate;
Vector3 m_DetachVelocity;
Vector3 m_DetachAngularVelocity;
int m_ThrowSmoothingCurrentFrame;
readonly float[] m_ThrowSmoothingFrameTimes = new float[k_ThrowSmoothingFrameCount];
readonly Vector3[] m_ThrowSmoothingVelocityFrames = new Vector3[k_ThrowSmoothingFrameCount];
readonly Vector3[] m_ThrowSmoothingAngularVelocityFrames = new Vector3[k_ThrowSmoothingFrameCount];
bool m_ThrowSmoothingFirstUpdate;
Pose m_LastThrowReferencePose;
IXRAimAssist m_ThrowAssist;
Rigidbody m_Rigidbody;
// Rigidbody's settings upon select, kept to restore these values when dropped
bool m_WasKinematic;
bool m_UsedGravity;
float m_OldDrag;
float m_OldAngularDrag;
// Used to keep track of colliders for which to ignore collision with character only while grabbed
bool m_IgnoringCharacterCollision;
bool m_StopIgnoringCollisionInLateUpdate;
CharacterController m_SelectingCharacterController;
readonly HashSet<IXRSelectInteractor> m_SelectingCharacterInteractors = new HashSet<IXRSelectInteractor>();
readonly List<Collider> m_RigidbodyColliders = new List<Collider>();
readonly HashSet<Collider> m_CollidersThatAllowedCharacterCollision = new HashSet<Collider>();
Transform m_OriginalSceneParent;
// Account for teleportation to avoid throws with unintentionally high energy
TeleportationMonitor m_TeleportationMonitor;
readonly Dictionary<IXRSelectInteractor, Transform> m_DynamicAttachTransforms = new Dictionary<IXRSelectInteractor, Transform>();
static readonly LinkedPool<Transform> s_DynamicAttachTransformPool = new LinkedPool<Transform>(OnCreatePooledItem, OnGetPooledItem, OnReleasePooledItem, OnDestroyPooledItem);
static readonly ProfilerMarker s_ProcessGrabTransformersMarker = new ProfilerMarker("XRI.ProcessGrabTransformers");
/// <inheritdoc />
protected override void Awake()
{
base.Awake();
m_TeleportationMonitor = new TeleportationMonitor();
m_TeleportationMonitor.teleported += OnTeleported;
m_CurrentMovementType = m_MovementType;
if (!TryGetComponent(out m_Rigidbody))
Debug.LogError("XR Grab Interactable does not have a required Rigidbody.", this);
m_Rigidbody.GetComponentsInChildren(true, m_RigidbodyColliders);
for (var i = m_RigidbodyColliders.Count - 1; i >= 0; i--)
{
if (m_RigidbodyColliders[i].attachedRigidbody != m_Rigidbody)
m_RigidbodyColliders.RemoveAt(i);
}
InitializeTargetPoseAndScale(transform);
if (m_AttachPointCompatibilityMode == AttachPointCompatibilityMode.Legacy)
{
#pragma warning disable 618 // Adding deprecated component to help with backwards compatibility with existing user projects.
var legacyGrabTransformer = GetOrAddComponent<XRLegacyGrabTransformer>();
#pragma warning restore 618
legacyGrabTransformer.enabled = true;
return;
}
// Load the starting grab transformers into the Play mode lists.
// It is more efficient to add than move, but if there are existing items
// use move to ensure the correct order dictated by the starting lists.
if (m_SingleGrabTransformers.flushedCount > 0)
{
var index = 0;
foreach (var transformer in m_StartingSingleGrabTransformers)
{
if (transformer != null)
MoveSingleGrabTransformerTo(transformer, index++);
}
}
else
{
foreach (var transformer in m_StartingSingleGrabTransformers)
{
if (transformer != null)
AddSingleGrabTransformer(transformer);
}
}
if (m_MultipleGrabTransformers.flushedCount > 0)
{
var index = 0;
foreach (var transformer in m_StartingMultipleGrabTransformers)
{
if (transformer != null)
MoveMultipleGrabTransformerTo(transformer, index++);
}
}
else
{
foreach (var transformer in m_StartingMultipleGrabTransformers)
{
if (transformer != null)
AddMultipleGrabTransformer(transformer);
}
}
FlushRegistration();
}
/// <inheritdoc />
protected override void OnDestroy()
{
// Unlink this interactable from the grab transformers
ClearSingleGrabTransformers();
ClearMultipleGrabTransformers();
base.OnDestroy();
}
/// <inheritdoc />
public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase updatePhase)
{
base.ProcessInteractable(updatePhase);
// Add the default grab transformers if needed.
// This is done here (as opposed to Awake) since transformer behaviors automatically register in their Start,
// so existing components should have a chance to register before we add the default grab transformers.
if (updatePhase == XRInteractionUpdateOrder.UpdatePhase.Dynamic)
AddDefaultGrabTransformers();
FlushRegistration();
switch (updatePhase)
{
// During Fixed update we want to apply any Rigidbody-based updates (e.g., Kinematic or VelocityTracking).
case XRInteractionUpdateOrder.UpdatePhase.Fixed:
if (isSelected || isTransformDirty)
{
if (m_CurrentMovementType == MovementType.Kinematic ||
m_CurrentMovementType == MovementType.VelocityTracking)
{
// If we only updated the target scale externally, just update that.
if (m_IsTargetLocalScaleDirty && !m_IsTargetPoseDirty && !isSelected)
ApplyTargetScale();
else if (m_CurrentMovementType == MovementType.Kinematic)
PerformKinematicUpdate(updatePhase);
else if (m_CurrentMovementType == MovementType.VelocityTracking)
PerformVelocityTrackingUpdate(updatePhase, Time.deltaTime);
}
}
if (m_IgnoringCharacterCollision && !m_StopIgnoringCollisionInLateUpdate &&
m_SelectingCharacterInteractors.Count == 0 && m_SelectingCharacterController != null &&
IsOutsideCharacterCollider(m_SelectingCharacterController))
{
// Wait until Late update so that physics can update before we restore the ability to collide with character
m_StopIgnoringCollisionInLateUpdate = true;
}
break;
// During Dynamic update and OnBeforeRender we want to update the target pose and apply any Transform-based updates (e.g., Instantaneous).
case XRInteractionUpdateOrder.UpdatePhase.Dynamic:
case XRInteractionUpdateOrder.UpdatePhase.OnBeforeRender:
if (isTransformDirty)
{
// If we only updated the target scale externally, just update that.
if (m_IsTargetLocalScaleDirty && !m_IsTargetPoseDirty)
ApplyTargetScale();
else
PerformInstantaneousUpdate(updatePhase);
}
if (isSelected || (m_GrabCountChanged && m_DropTransformersCount > 0))
{
UpdateTarget(updatePhase, Time.deltaTime);
if (m_CurrentMovementType == MovementType.Instantaneous)
PerformInstantaneousUpdate(updatePhase);
}
break;
// Late update is used to handle detach and restoring character collision as late as possible.
case XRInteractionUpdateOrder.UpdatePhase.Late:
if (m_DetachInLateUpdate)
{
if (!isSelected)
Detach();
m_DetachInLateUpdate = false;
}
if (m_StopIgnoringCollisionInLateUpdate)
{
if (m_IgnoringCharacterCollision && m_SelectingCharacterController != null)
{
StopIgnoringCharacterCollision(m_SelectingCharacterController);
m_SelectingCharacterController = null;
}
m_StopIgnoringCollisionInLateUpdate = false;
}
break;
}
}
/// <inheritdoc />
public override Transform GetAttachTransform(IXRInteractor interactor)
{
bool isFirst = interactorsSelecting.Count <= 1 || ReferenceEquals(interactor, interactorsSelecting[0]);
// If first selector, do normal behavior.
// If second, we ignore dynamic attach setting if there is no secondary attach transform.
var shouldUseDynamicAttach = m_UseDynamicAttach || (!isFirst && m_SecondaryAttachTransform == null);
if (shouldUseDynamicAttach && interactor is IXRSelectInteractor selectInteractor &&
m_DynamicAttachTransforms.TryGetValue(selectInteractor, out var dynamicAttachTransform))
{
if (dynamicAttachTransform != null)
return dynamicAttachTransform;
m_DynamicAttachTransforms.Remove(selectInteractor);
Debug.LogWarning($"Dynamic Attach Transform created by {this} for {interactor} was destroyed after being created." +
" Continuing as if Use Dynamic Attach was disabled for this pair.", this);
}
// If not first, and not using dynamic attach, then we must have a secondary attach transform set.
if (!isFirst && !shouldUseDynamicAttach)
{
return m_SecondaryAttachTransform;
}
return m_AttachTransform != null ? m_AttachTransform : base.GetAttachTransform(interactor);
}
/// <summary>
/// Adds the given grab transformer to the list of transformers used when there is a single interactor selecting this object.
/// </summary>
/// <param name="transformer">The grab transformer to add.</param>
/// <seealso cref="AddMultipleGrabTransformer"/>
public void AddSingleGrabTransformer(IXRGrabTransformer transformer) => AddGrabTransformer(transformer, m_SingleGrabTransformers);
/// <summary>
/// Adds the given grab transformer to the list of transformers used when there are multiple interactors selecting this object.
/// </summary>
/// <param name="transformer">The grab transformer to add.</param>
/// <seealso cref="AddSingleGrabTransformer"/>
public void AddMultipleGrabTransformer(IXRGrabTransformer transformer) => AddGrabTransformer(transformer, m_MultipleGrabTransformers);
/// <summary>
/// Removes the given grab transformer from the list of transformers used when there is a single interactor selecting this object.
/// </summary>
/// <param name="transformer">The grab transformer to remove.</param>
/// <returns>
/// Returns <see langword="true"/> if <paramref name="transformer"/> was removed from the list.
/// Otherwise, returns <see langword="false"/> if <paramref name="transformer"/> was not found in the list.
/// </returns>
/// <seealso cref="RemoveMultipleGrabTransformer"/>
public bool RemoveSingleGrabTransformer(IXRGrabTransformer transformer) => RemoveGrabTransformer(transformer, m_SingleGrabTransformers);