TrackedPoseDriver.cs
22.9 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
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
#if ENABLE_VR || ENABLE_AR
using UnityEngine.XR;
using UnityEngine.Experimental.XR.Interaction;
#endif
#if ENABLE_AR
using UnityEngine.XR.Tango;
#endif
[assembly: InternalsVisibleTo("UnityEditor.SpatialTracking")]
namespace UnityEngine.SpatialTracking
{
internal class TrackedPoseDriverDataDescription
{
internal struct PoseData
{
public List<string> PoseNames;
public List<TrackedPoseDriver.TrackedPose> Poses;
}
internal static List<PoseData> DeviceData = new List<PoseData>
{
// Generic XR Device
new PoseData
{
PoseNames = new List<string>
{
"Left Eye", "Right Eye", "Center Eye - HMD Reference", "Head", "Color Camera"
},
Poses = new List<TrackedPoseDriver.TrackedPose>
{
TrackedPoseDriver.TrackedPose.LeftEye,
TrackedPoseDriver.TrackedPose.RightEye,
TrackedPoseDriver.TrackedPose.Center,
TrackedPoseDriver.TrackedPose.Head,
TrackedPoseDriver.TrackedPose.ColorCamera
}
},
// generic controller
new PoseData
{
PoseNames = new List<string>
{
"Left Controller", "Right Controller"
},
Poses = new List<TrackedPoseDriver.TrackedPose>
{
TrackedPoseDriver.TrackedPose.LeftPose,
TrackedPoseDriver.TrackedPose.RightPose
}
},
// generic remote
new PoseData
{
PoseNames = new List<string>
{
"Device Pose"
},
Poses = new List<TrackedPoseDriver.TrackedPose>
{
TrackedPoseDriver.TrackedPose.RemotePose,
}
},
};
}
/// <summary>
/// Bitflag enum which represents what data was set on an associated Pose struct
/// </summary>
[Flags]
public enum PoseDataFlags
{
/// <summary>
/// No data was actually set on the pose
/// </summary>
NoData = 0,
/// <summary>
/// If this flag is set, position data was updated on the associated pose struct
/// </summary>
Position = 1 << 0,
/// <summary>
/// If this flag is set, rotation data was updated on the associated pose struct
/// </summary>
Rotation = 1 << 1,
}
/// <summary>
/// The PoseDataSource class acts as a container for the GetDatafromSource method call that should be used by PoseProviders wanting to query data for a particular pose.
/// </summary>
static public class PoseDataSource
{
#if ENABLE_AR || ENABLE_VR
static internal List<XR.XRNodeState> nodeStates = new List<XR.XRNodeState>();
static internal PoseDataFlags GetNodePoseData(XR.XRNode node, out Pose resultPose)
{
PoseDataFlags retData = PoseDataFlags.NoData;
XR.InputTracking.GetNodeStates(nodeStates);
foreach (XR.XRNodeState nodeState in nodeStates)
{
if (nodeState.nodeType == node)
{
if(nodeState.TryGetPosition(out resultPose.position))
{
retData |= PoseDataFlags.Position;
}
if (nodeState.TryGetRotation(out resultPose.rotation))
{
retData |= PoseDataFlags.Rotation;
}
return retData;
}
}
resultPose = Pose.identity;
return retData;
}
#endif
/// <summary>The GetDatafromSource method is used to query data from the XRNode subsystem based on the provided pose source.</summary>
/// <param name = "poseSource" > The pose source to request data for.</param>
/// <param name = "resultPose" > The resulting pose data.</param>
/// <returns>True, if the pose source is valid, otherwise false.</returns>
static public bool TryGetDataFromSource(TrackedPoseDriver.TrackedPose poseSource, out Pose resultPose)
{
return GetDataFromSource(poseSource, out resultPose) == (PoseDataFlags.Position | PoseDataFlags.Rotation);
}
/// <summary>The GetDatafromSource method is used to query data from the XRNode subsystem based on the provided pose source.</summary>
/// <param name = "poseSource" > The pose source to request data for.</param>
/// <param name = "resultPose" > The resulting pose data. This function will return the Center Eye pose if the Color Camera pose is not available. </param>
/// <returns>Retuns a bitflag which represents which data has been retrieved from the provided pose source</returns>
static public PoseDataFlags GetDataFromSource(TrackedPoseDriver.TrackedPose poseSource, out Pose resultPose)
{
#if ENABLE_AR || ENABLE_VR
switch (poseSource)
{
case TrackedPoseDriver.TrackedPose.RemotePose:
{
PoseDataFlags retFlags = GetNodePoseData(XR.XRNode.RightHand, out resultPose);
if (retFlags == PoseDataFlags.NoData)
return GetNodePoseData(XR.XRNode.LeftHand, out resultPose);
return retFlags;
}
case TrackedPoseDriver.TrackedPose.LeftEye:
{
return GetNodePoseData(XR.XRNode.LeftEye, out resultPose);
}
case TrackedPoseDriver.TrackedPose.RightEye:
{
return GetNodePoseData(XR.XRNode.RightEye, out resultPose);
}
case TrackedPoseDriver.TrackedPose.Head:
{
return GetNodePoseData(XR.XRNode.Head, out resultPose);
}
case TrackedPoseDriver.TrackedPose.Center:
{
return GetNodePoseData(XR.XRNode.CenterEye, out resultPose);
}
case TrackedPoseDriver.TrackedPose.LeftPose:
{
return GetNodePoseData(XR.XRNode.LeftHand, out resultPose);
}
case TrackedPoseDriver.TrackedPose.RightPose:
{
return GetNodePoseData(XR.XRNode.RightHand, out resultPose);
}
case TrackedPoseDriver.TrackedPose.ColorCamera:
{
PoseDataFlags retFlags = TryGetTangoPose(out resultPose);
if(retFlags == PoseDataFlags.NoData)
{
// We fall back to CenterEye because we can't currently extend the XRNode structure, nor are we ready to replace it.
return GetNodePoseData(XR.XRNode.CenterEye, out resultPose);
}
return retFlags;
}
default:
{
Debug.LogWarningFormat("Unable to retrieve pose data for poseSource: {0}", poseSource.ToString());
break;
}
}
#endif
resultPose = Pose.identity;
return PoseDataFlags.NoData;
}
static PoseDataFlags TryGetTangoPose(out Pose pose)
{
#if ENABLE_AR
PoseData poseOut;
if (TangoInputTracking.TryGetPoseAtTime(out poseOut) && poseOut.statusCode == PoseStatus.Valid)
{
pose.position = poseOut.position;
pose.rotation = poseOut.rotation;
return PoseDataFlags.Position | PoseDataFlags.Rotation; ;
}
#endif
pose = Pose.identity;
return PoseDataFlags.NoData;
}
}
// The DefaultExecutionOrder is needed because TrackedPoseDriver does some
// of its work in regular Update and FixedUpdate calls, but this needs to
// be done before regular user scripts have their own Update and
// FixedUpdate calls, in order that they correctly get the values for this
// frame and not the previous.
// -32000 is the minimal possible execution order value; -30000 makes it
// is unlikely users chose lower values for their scripts by accident, but
// still makes it possible.
/// <summary>
/// The TrackedPoseDriver component applies the current Pose value of a tracked device to the transform of the GameObject.
/// TrackedPoseDriver can track multiple types of devices including XR HMDs, controllers, and remotes.
/// </summary>
[DefaultExecutionOrder(-30000)]
[Serializable]
[AddComponentMenu("XR/Tracked Pose Driver")]
[HelpURL("https://docs.unity3d.com/Packages/com.unity.xr.legacyinputhelpers@2.1/manual/index.html")]
public class TrackedPoseDriver : MonoBehaviour
{
/// <summary>
/// The device being tracked by the tracked pose driver
/// </summary>
public enum DeviceType
{
/// <summary>
/// An XR Controller, use this value for controllers
/// </summary>
GenericXRDevice = 0,
/// <summary>
/// An Generic XR Devices, use this value for HMD and AR Mobile device tracking
/// </summary>
GenericXRController = 1,
/// <summary>
/// An XR Remote, use this value for mobile remotes
/// </summary>
GenericXRRemote = 2
}
/// <summary>
/// The list of endpoints that users can track with the <see cref="TrackedPoseDriver"/>
/// </summary>
public enum TrackedPose
{
/// <summary>
/// The left eye of a HMD style device
/// </summary>
LeftEye = 0,
/// <summary>
/// The right eye of a HMD style device
/// </summary>
RightEye = 1,
/// <summary>
/// The center eye of a HMD style device, this is usually the default for most HMDs
/// </summary>
Center = 2,
/// <summary>
/// The head eye of a HMD style device
/// </summary>
Head = 3,
/// <summary>
/// The left hand controller pose
/// </summary>
LeftPose = 4,
/// <summary>
/// The right hand controller pose
/// </summary>
RightPose = 5,
/// <summary>
/// The color camera of a mobile device
/// </summary>
ColorCamera = 6,
/// <summary>
/// No Longer Used
/// </summary>
DepthCameraDeprecated = 7,
/// <summary>
/// No Longer Used
/// </summary>
FisheyeCameraDeprected = 8,
/// <summary>
/// No Longer Used
/// </summary>
DeviceDeprecated = 9,
/// <summary>
/// The pose of a mobile remote
/// </summary>
RemotePose = 10,
}
[SerializeField]
DeviceType m_Device;
/// <summary>
/// This is used to indicate which pose the TrackedPoseDriver is currently tracking.
/// </summary>
public DeviceType deviceType
{
get { return m_Device; }
internal set { m_Device = value; }
}
[SerializeField]
TrackedPose m_PoseSource = TrackedPoseDriver.TrackedPose.Center;
/// <summary>
/// The pose being tracked by the tracked pose driver
/// </summary>
public TrackedPose poseSource
{
get { return m_PoseSource; }
internal set { m_PoseSource = value; }
}
/// <summary>
/// This method is used to set the device / pose pair for the SpatialTracking.TrackedPoseDriver. setting an invalid combination of these values will return false.
/// </summary>
/// <param name="deviceType">The device type that we wish to track </param>
/// <param name="pose">The pose source that we wish to track</param>
/// <returns>true if the values provided are sensible, otherwise false</returns>
public bool SetPoseSource(DeviceType deviceType, TrackedPose pose)
{
if ((int)deviceType < TrackedPoseDriverDataDescription.DeviceData.Count)
{
TrackedPoseDriverDataDescription.PoseData val = TrackedPoseDriverDataDescription.DeviceData[(int)deviceType];
for (int i = 0; i < val.Poses.Count; ++i)
{
if (val.Poses[i] == pose)
{
this.deviceType = deviceType;
poseSource = pose;
return true;
}
}
}
return false;
}
#if ENABLE_VR || ENABLE_AR
[SerializeField]
BasePoseProvider m_PoseProviderComponent = null;
/// <summary>
/// Optional: This field holds the reference to the PoseProvider instance that, if set, will be used to override the behavior of
/// the TrackedPoseDriver. When this field is empty, the TrackedPoseDriver will operate as per usual, with pose data being
/// retrieved from the device or pose settings of the TrackedPoseDriver. When this field is set, the pose data will be
/// provided by the attached PoseProvider. The device or pose fields will be hidden as they are no longer used to
/// control the parent GameObject Transform.
/// </summary>
public BasePoseProvider poseProviderComponent
{
get { return m_PoseProviderComponent; }
set
{
m_PoseProviderComponent = value;
}
}
#endif
PoseDataFlags GetPoseData(DeviceType device, TrackedPose poseSource, out Pose resultPose)
{
#if ENABLE_VR || ENABLE_AR
if (m_PoseProviderComponent != null)
{
return m_PoseProviderComponent.GetPoseFromProvider(out resultPose);
}
#endif
return PoseDataSource.GetDataFromSource(poseSource, out resultPose);
}
/// <summary>
/// This enum is used to indicate which parts of the pose will be applied to the parent transform
/// </summary>
public enum TrackingType
{
/// <summary>
/// With this setting, both the pose's rotation and position will be applied to the parent transform
/// </summary>
RotationAndPosition,
/// <summary>
/// With this setting, only the pose's rotation will be applied to the parent transform
/// </summary>
RotationOnly,
/// <summary>
/// With this setting, only the pose's position will be applied to the parent transform
/// </summary>
PositionOnly
}
[SerializeField]
TrackingType m_TrackingType;
/// <summary>
/// The tracking type being used by the tracked pose driver
/// </summary>
public TrackingType trackingType
{
get { return m_TrackingType; }
set { m_TrackingType = value; }
}
/// <summary>
/// The update type being used by the tracked pose driver
/// </summary>
public enum UpdateType
{
/// <summary>
/// Sample input at both update, and directly before rendering. For smooth head pose tracking,
/// we recommend using this value as it will provide the lowest input latency for the device.
/// This is the default value for the UpdateType option
/// </summary>
UpdateAndBeforeRender,
/// <summary>
/// Only sample input during the update phase of the frame.
/// </summary>
Update,
/// <summary>
/// Only sample input directly before rendering
/// </summary>
BeforeRender,
}
[SerializeField]
UpdateType m_UpdateType = UpdateType.UpdateAndBeforeRender;
/// <summary>
/// The update type being used by the tracked pose driver
/// </summary>
public UpdateType updateType
{
get { return m_UpdateType; }
set { m_UpdateType = value; }
}
[SerializeField]
bool m_UseRelativeTransform = false;
/// <summary>
/// This is used to indicate whether the TrackedPoseDriver will use the object's original transform as its basis.
/// </summary>
public bool UseRelativeTransform
{
get { return m_UseRelativeTransform; }
set { m_UseRelativeTransform = value; }
}
/// <summary>
/// The origin pose is the offset applied to any tracking data. This is only used when in legacy compatibility mode.
/// </summary>
protected Pose m_OriginPose;
/// <summary>
/// originPose is an offset applied to any tracking data read from this object.
/// Setting this value should be reserved for dealing with edge-cases, such as
/// achieving parity between room-scale (floor centered) and stationary (head centered)
/// tracking - without having to alter the transform hierarchy.
/// For user locomotion and gameplay purposes you are usually better off just
/// moving the parent transform of this object.
/// </summary>
public Pose originPose
{
get { return m_OriginPose; }
set { m_OriginPose = value; }
}
private void CacheLocalPosition()
{
m_OriginPose.position = transform.localPosition;
m_OriginPose.rotation = transform.localRotation;
}
private void ResetToCachedLocalPosition()
{
SetLocalTransform(m_OriginPose.position, m_OriginPose.rotation, PoseDataFlags.Position | PoseDataFlags.Rotation);
}
/// <inheritdoc />
protected virtual void Awake()
{
CacheLocalPosition();
#if UNITY_2019_3_OR_NEWER
// deprecated functionality in 2020.1
#else
if (HasStereoCamera())
{
#if ENABLE_AR || ENABLE_VR
XRDevice.DisableAutoXRCameraTracking(GetComponent<Camera>(), true);
#endif
}
#endif
}
/// <inheritdoc />
protected virtual void OnDestroy()
{
#if UNITY_2019_3_OR_NEWER
// deprecated functionality in 2020.1
#else
if (HasStereoCamera())
{
#if ENABLE_AR || ENABLE_VR
XRDevice.DisableAutoXRCameraTracking(GetComponent<Camera>(), false);
#endif
}
#endif
}
/// <inheritdoc />
protected virtual void OnEnable()
{
Application.onBeforeRender += OnBeforeRender;
}
/// <inheritdoc />
protected virtual void OnDisable()
{
// remove delegate registration
ResetToCachedLocalPosition();
Application.onBeforeRender -= OnBeforeRender;
}
/// <inheritdoc />
protected virtual void FixedUpdate()
{
if (m_UpdateType == UpdateType.Update ||
m_UpdateType == UpdateType.UpdateAndBeforeRender)
{
PerformUpdate();
}
}
/// <inheritdoc />
protected virtual void Update()
{
if (m_UpdateType == UpdateType.Update ||
m_UpdateType == UpdateType.UpdateAndBeforeRender)
{
PerformUpdate();
}
}
/// <inheritdoc />
protected virtual void OnBeforeRender()
{
if (m_UpdateType == UpdateType.BeforeRender ||
m_UpdateType == UpdateType.UpdateAndBeforeRender)
{
PerformUpdate();
}
}
/// <summary>
/// Sets the transform that is being driven by the <see cref="TrackedPoseDriver"/>. will only correct set the rotation or position depending on the <see cref="PoseDataFlags"/>
/// </summary>
/// <param name="newPosition">The position to apply.</param>
/// <param name="newRotation">The rotation to apply.</param>
/// <param name="poseFlags">The flags indiciating which of the position/rotation values are provided by the calling code.</param>
protected virtual void SetLocalTransform(Vector3 newPosition, Quaternion newRotation, PoseDataFlags poseFlags)
{
if ((m_TrackingType == TrackingType.RotationAndPosition ||
m_TrackingType == TrackingType.RotationOnly) &&
(poseFlags & PoseDataFlags.Rotation) > 0)
{
transform.localRotation = newRotation;
}
if ((m_TrackingType == TrackingType.RotationAndPosition ||
m_TrackingType == TrackingType.PositionOnly) &&
(poseFlags & PoseDataFlags.Position) > 0)
{
transform.localPosition = newPosition;
}
}
/// <summary>
/// This is only used when running in legacy mode, and will fake the behavior of the old implicit camera tracking. This will transform by the origin pose if necessary.
/// </summary>
/// <param name="pose">Pose to transform by the origin if in relative transform mode.</param>
/// <returns>The pose, with the applied transform if in Relative Transform mode.</returns>
protected Pose TransformPoseByOriginIfNeeded(Pose pose)
{
if (m_UseRelativeTransform)
{
return pose.GetTransformedBy(m_OriginPose);
}
else
{
return pose;
}
}
private bool HasStereoCamera()
{
Camera camera = GetComponent<Camera>();
return camera != null && camera.stereoEnabled;
}
/// <summary>
/// PerformUpdate queries the data from the selected pose source, and then calls <see cref="SetLocalTransform"/> to apply the pose.
/// </summary>
protected virtual void PerformUpdate()
{
if (!enabled)
return;
Pose currentPose = new Pose();
currentPose = Pose.identity;
PoseDataFlags poseFlags = GetPoseData(m_Device, m_PoseSource, out currentPose);
if(poseFlags != PoseDataFlags.NoData)
{
Pose localPose = TransformPoseByOriginIfNeeded(currentPose);
SetLocalTransform(localPose.position, localPose.rotation, poseFlags);
}
}
}
}