Tanoda
LeapXRServiceProvider.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
3 * *
4 * Use subject to the terms of the Apache License 2.0 available at *
5 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6 * between Ultraleap and you, your company or other organization. *
7 ******************************************************************************/
8
9using UnityEngine;
10using System;
12
13#if UNITY_2019_1_OR_NEWER
14using UnityEngine.Rendering;
15#endif
16
17namespace Leap.Unity {
18
26
27 #region Inspector
28
29 // Manual Device Offset
30
31 private const float DEFAULT_DEVICE_OFFSET_Y_AXIS = 0f;
32 private const float DEFAULT_DEVICE_OFFSET_Z_AXIS = 0.12f;
33 private const float DEFAULT_DEVICE_TILT_X_AXIS = 5f;
34
35 public enum DeviceOffsetMode {
36 Default,
37 ManualHeadOffset,
38 Transform
39 }
40
41 [Tooltip("Allow manual adjustment of the Leap device's virtual offset and tilt. These "
42 + "settings can be used to match the physical position and orientation of the "
43 + "Leap Motion sensor on a tracked device it is mounted on (such as a VR "
44 + "headset). ")]
45 [SerializeField, OnEditorChange("deviceOffsetMode")]
46 private DeviceOffsetMode _deviceOffsetMode;
48 get { return _deviceOffsetMode; }
49 set {
50 _deviceOffsetMode = value;
51 if (_deviceOffsetMode == DeviceOffsetMode.Default) {
52 deviceOffsetYAxis = DEFAULT_DEVICE_OFFSET_Y_AXIS;
53 deviceOffsetZAxis = DEFAULT_DEVICE_OFFSET_Z_AXIS;
54 deviceTiltXAxis = DEFAULT_DEVICE_TILT_X_AXIS;
55 }
56 }
57 }
58
59 [Tooltip("Adjusts the Leap Motion device's virtual height offset from the tracked "
60 + "headset position. This should match the vertical offset of the physical "
61 + "device with respect to the headset in meters.")]
62 [SerializeField]
63 [Range(-0.50F, 0.50F)]
64 private float _deviceOffsetYAxis = DEFAULT_DEVICE_OFFSET_Y_AXIS;
65 public float deviceOffsetYAxis {
66 get { return _deviceOffsetYAxis; }
67 set { _deviceOffsetYAxis = value; }
68 }
69
70 [Tooltip("Adjusts the Leap Motion device's virtual depth offset from the tracked "
71 + "headset position. This should match the forward offset of the physical "
72 + "device with respect to the headset in meters.")]
73 [SerializeField]
74 [Range(-0.50F, 0.50F)]
75 private float _deviceOffsetZAxis = DEFAULT_DEVICE_OFFSET_Z_AXIS;
76 public float deviceOffsetZAxis {
77 get { return _deviceOffsetZAxis; }
78 set { _deviceOffsetZAxis = value; }
79 }
80
81 [Tooltip("Adjusts the Leap Motion device's virtual X axis tilt. This should match "
82 + "the tilt of the physical device with respect to the headset in degrees.")]
83 [SerializeField]
84 [Range(-90.0F, 90.0F)]
85 private float _deviceTiltXAxis = DEFAULT_DEVICE_TILT_X_AXIS;
86 public float deviceTiltXAxis {
87 get { return _deviceTiltXAxis; }
88 set { _deviceTiltXAxis = value; }
89 }
90
91 [Tooltip("Allows for the manual placement of the Leap Tracking Device." +
92 "This device offset mode is incompatible with Temporal Warping.")]
93 [SerializeField]
94 private Transform _deviceOrigin;
95 public Transform deviceOrigin {
96 get { return _deviceOrigin; }
97 set { _deviceOrigin = value; }
98 }
99
100 [Tooltip("This camera's PreCull time triggers pose data collection for "
101 + "temporal warping. This needs to be set if the provider is not "
102 + "on the same GameObject as the main Camera component.")]
103 [SerializeField]
104 private Camera _preCullCamera;
105 public Camera preCullCamera {
106 get { return _preCullCamera; }
107 set { _preCullCamera = value; }
108 }
109
110 // Temporal Warping
111
112#if UNITY_STANDALONE
113 private const int DEFAULT_WARP_ADJUSTMENT = 17;
114#elif UNITY_ANDROID
115 private const int DEFAULT_WARP_ADJUSTMENT = 45;
116#else
117 private const int DEFAULT_WARP_ADJUSTMENT = 17;
118#endif
119
121 Auto,
122 Manual,
123 Images,
124 Off
125 }
126 [Tooltip("Temporal warping prevents the hand coordinate system from 'swimming' or "
127 + "'bouncing' when the headset moves and the user's hands stay still. "
128 + "This phenomenon is caused by the differing amounts of latencies inherent "
129 + "in the two systems. "
130 + "For PC VR and Android VR, temporal warping should set to 'Auto', as the "
131 + "correct value can be chosen automatically for these platforms. "
132 + "Some non-standard platforms may use 'Manual' mode to adjust their "
133 + "latency compensation amount for temporal warping. "
134 + "Use 'Images' for scenarios that overlay Leap device images on tracked "
135 + "hand data.")]
136 [SerializeField]
137 private TemporalWarpingMode _temporalWarpingMode = TemporalWarpingMode.Auto;
138
143 [Tooltip("The time in milliseconds between the current frame's headset position and "
144 + "the time at which the Leap frame was captured.")]
145 [SerializeField]
146 private int _customWarpAdjustment = DEFAULT_WARP_ADJUSTMENT;
147 public int warpingAdjustment {
148 get {
149 if (_temporalWarpingMode == TemporalWarpingMode.Manual) {
150 return _customWarpAdjustment;
151 } else {
152 return DEFAULT_WARP_ADJUSTMENT;
153 }
154 }
155 set {
156 if (_temporalWarpingMode != TemporalWarpingMode.Manual) {
157 Debug.LogWarning("Setting custom warping adjustment amount, but the "
158 + "temporal warping mode is not Manual, so the value will be "
159 + "ignored.");
160 }
161 _customWarpAdjustment = value;
162 }
163 }
164
165 // Pre-cull Latching
166
167 [Tooltip("Pass updated transform matrices to hands with materials that utilize the "
168 + "VertexOffsetShader. Won't have any effect on hands that don't take into "
169 + "account shader-global vertex offsets in their material shaders.")]
170 [SerializeField]
171 protected bool _updateHandInPrecull = false;
173 get { return _updateHandInPrecull; }
174 set {
176 _updateHandInPrecull = value;
177 }
178 }
179
180 #endregion
181
182 #region Internal Memory
183
186 protected Vector3 warpedPosition = Vector3.zero;
187 protected Quaternion warpedRotation = Quaternion.identity;
188 protected Matrix4x4[] _transformArray = new Matrix4x4[2];
189 private Pose? _trackingBaseDeltaPose = null;
190
191 private Camera _cachedCamera;
192 private Camera cachedCamera {
193 get {
194 if (_cachedCamera == null) {
195 _cachedCamera = GetComponent<Camera>();
196 }
197 return _cachedCamera;
198 }
199 }
200
201 [NonSerialized]
202 public long imageTimeStamp = 0;
203
204 #endregion
205
206 #region Unity Events
207
208 protected override void Reset() {
209 base.Reset();
210 editTimePose = TestHandFactory.TestHandPose.HeadMountedB;
211
212 if (preCullCamera == null) {
213 preCullCamera = GetComponent<Camera>();
214 }
215 }
216
217 // TODO: DELETEME
218 // protected virtual void OnValidate() {
219 // if (_deviceOffsetMode == DeviceOffsetMode.Transform &&
220 // _temporalWarpingMode != TemporalWarpingMode.Off) {
221 // #if UNITY_EDITOR
222 // UnityEditor.Undo.RecordObject(this, "Disabled Temporal Warping");
223 // Debug.LogWarning("Temporal warping disabled. Temporal warping cannot be used "
224 // + "with the Transform device offset mode.", this);
225 // #endif
226 // _temporalWarpingMode = TemporalWarpingMode.Off;
227 // }
228 // }
229
230 protected virtual void OnEnable() {
232
233 if (preCullCamera == null) {
234 preCullCamera = GetComponent<Camera>();
235 }
236
237 #if XR_LEGACY_INPUT_AVAILABLE
238 if (GetComponent<UnityEngine.SpatialTracking.TrackedPoseDriver>() == null) {
239 gameObject.AddComponent<UnityEngine.SpatialTracking.TrackedPoseDriver>().UseRelativeTransform = true;
240 }
241 #endif
242
243 #if UNITY_2019_1_OR_NEWER
244 if (GraphicsSettings.renderPipelineAsset != null) {
245 RenderPipelineManager.beginCameraRendering -= onBeginRendering;
246 RenderPipelineManager.beginCameraRendering += onBeginRendering;
247 } else {
248 Camera.onPreCull -= onPreCull; // No multiple-subscription.
249 Camera.onPreCull += onPreCull;
250 }
251 #else
252 Camera.onPreCull -= onPreCull; // No multiple-subscription.
253 Camera.onPreCull += onPreCull;
254 #endif
255 }
256
257 protected virtual void OnDisable() {
259
260 #if UNITY_2019_1_OR_NEWER
261 if (GraphicsSettings.renderPipelineAsset != null) {
262 RenderPipelineManager.beginCameraRendering -= onBeginRendering;
263 } else {
264 Camera.onPreCull -= onPreCull; // No multiple-subscription.
265 }
266 #else
267 Camera.onPreCull -= onPreCull; // No multiple-subscription.
268 #endif
269 }
270
271 protected override void Start() {
272 base.Start();
273 _cachedCamera = GetComponent<Camera>();
274 if (_deviceOffsetMode == DeviceOffsetMode.Transform && _deviceOrigin == null) {
275 Debug.LogError("Cannot use the Transform device offset mode without " +
276 "specifying a Transform to use as the device origin.", this);
277 _deviceOffsetMode = DeviceOffsetMode.Default;
278 }
279
280 if (Application.isPlaying && preCullCamera == null &&
281 _temporalWarpingMode != TemporalWarpingMode.Off) {
282 Debug.LogError("Cannot perform temporal warping with no pre-cull camera.");
283 }
284 }
285
286 protected override void Update() {
288 base.Update();
290 }
291
292 void LateUpdate() {
293 var projectionMatrix = _cachedCamera == null ? Matrix4x4.identity
294 : _cachedCamera.projectionMatrix;
295 switch (SystemInfo.graphicsDeviceType) {
296 #if !UNITY_2017_2_OR_NEWER
297 case UnityEngine.Rendering.GraphicsDeviceType.Direct3D9:
298 #endif
299 case UnityEngine.Rendering.GraphicsDeviceType.Direct3D11:
300 case UnityEngine.Rendering.GraphicsDeviceType.Direct3D12:
301 for (int i = 0; i < 4; i++) {
302 projectionMatrix[1, i] = -projectionMatrix[1, i];
303 }
304 // Scale and bias from OpenGL -> D3D depth range
305 for (int i = 0; i < 4; i++) {
306 projectionMatrix[2, i] = projectionMatrix[2, i] * 0.5f
307 + projectionMatrix[3, i] * 0.5f;
308 }
309 break;
310 }
311
312 // Update Image Warping
313 Vector3 pastPosition; Quaternion pastRotation;
315 - (long)(warpingAdjustment * 1000f),
316 out pastPosition, out pastRotation);
317
318 // Use _tweenImageWarping
319 var currCenterRotation = XRSupportUtil.GetXRNodeCenterEyeLocalRotation();
320
321 var imageReferenceRotation = _temporalWarpingMode != TemporalWarpingMode.Off
322 ? pastRotation
323 : currCenterRotation;
324
325 Quaternion imageQuatWarp = Quaternion.Inverse(currCenterRotation)
326 * imageReferenceRotation;
327 imageQuatWarp = Quaternion.Euler(imageQuatWarp.eulerAngles.x,
328 imageQuatWarp.eulerAngles.y,
329 -imageQuatWarp.eulerAngles.z);
330 Matrix4x4 imageMatWarp = projectionMatrix
331 #if UNITY_2019_2_OR_NEWER
332 // The camera projection matrices seem to have vertically inverted...
333 * Matrix4x4.TRS(Vector3.zero, imageQuatWarp, new Vector3(1f, -1f, 1f))
334 #else
335 * Matrix4x4.TRS(Vector3.zero, imageQuatWarp, Vector3.one)
336 #endif
337 * projectionMatrix.inverse;
338 Shader.SetGlobalMatrix("_LeapGlobalWarpedOffset", imageMatWarp);
339 }
340
341 #if UNITY_2019_1_OR_NEWER
342 protected virtual void onBeginRendering(ScriptableRenderContext context, Camera camera) { onPreCull(camera); }
343 #endif
344
345 protected virtual void onPreCull(Camera preCullingCamera) {
346 if (preCullingCamera != preCullCamera) {
347 return;
348 }
349
350 #if UNITY_EDITOR
351 if (!Application.isPlaying) {
352 return;
353 }
354 #endif
355
356 Pose trackedPose;
357 if (_deviceOffsetMode == DeviceOffsetMode.Default
358 || _deviceOffsetMode == DeviceOffsetMode.ManualHeadOffset) {
359 // Get most recent tracked pose from the XR subsystem.
360 trackedPose = new Pose(XRSupportUtil.GetXRNodeCenterEyeLocalPosition(),
361 XRSupportUtil.GetXRNodeCenterEyeLocalRotation());
362
363 // If we don't know of any pose offset yet, account for it by finding
364 // the pose delta from the "local" tracked pose to the actual camera
365 // pose.
366 if (!_trackingBaseDeltaPose.HasValue) {
367 _trackingBaseDeltaPose = _cachedCamera.transform.ToLocalPose()
368 * trackedPose.inverse;
369 }
370 // This way, we always track a scene-space tracked pose.
371 trackedPose = _trackingBaseDeltaPose.Value * trackedPose;
372 }
373 else if (_deviceOffsetMode == DeviceOffsetMode.Transform) {
374 trackedPose = deviceOrigin.ToPose();
375 }
376 else {
377 Debug.LogError("Unsupported DeviceOffsetMode: " + _deviceOffsetMode);
378 return;
379 }
380
382
383 OnPreCullHandTransforms(_cachedCamera);
384 }
385
386 #endregion
387
388 #region LeapServiceProvider Overrides
389
390 protected override long CalculateInterpolationTime(bool endOfFrame = false) {
391#if UNITY_ANDROID
392 return _leapController.Now() - 16000;
393#else
394 if (_leapController != null) {
395 return _leapController.Now()
397 + ((updateHandInPrecull && !endOfFrame) ?
398 (long)(Time.smoothDeltaTime * S_TO_NS / Time.timeScale)
399 : 0);
400 } else {
401 return 0;
402 }
403#endif
404 }
405
410 protected override void initializeFlags() {
411 if (_leapController == null) {
412 return;
413 }
414
415 // Optimize for head-mounted tracking if on head-mounted display.
416 _leapController.ClearPolicy(Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP);
417 _leapController.SetPolicy(Controller.PolicyFlag.POLICY_OPTIMIZE_HMD);
418 }
419
420 protected override void transformFrame(Frame source, Frame dest) {
421 LeapTransform leapTransform = GetWarpedMatrix(source.Timestamp);
422 dest.CopyFrom(source).Transform(leapTransform);
423 }
424
425 #endregion
426
427 #region Internal Methods
428
432 protected void resetShaderTransforms() {
433 _transformArray[0] = Matrix4x4.identity;
434 _transformArray[1] = Matrix4x4.identity;
435 Shader.SetGlobalMatrixArray(HAND_ARRAY_GLOBAL_NAME, _transformArray);
436 }
437
438 protected virtual LeapTransform GetWarpedMatrix(long timestamp,
439 bool updateTemporalCompensation = true) {
440 LeapTransform leapTransform;
441
442 //Calculate a Temporally Warped Pose
443 if (Application.isPlaying
444 && updateTemporalCompensation
445 && transformHistory.history.IsFull
446 && _temporalWarpingMode != TemporalWarpingMode.Off) {
448 - (long)(warpingAdjustment * 1000f)
449 - (_temporalWarpingMode ==
450 TemporalWarpingMode.Images ? -20000 : 0),
452 }
453
454 // Normalize the rotation Quaternion.
455 warpedRotation = warpedRotation.ToNormalized();
456
457 //Calculate the Current Pose
458 Pose currentPose = Pose.identity;
459 if (_deviceOffsetMode == DeviceOffsetMode.Transform && deviceOrigin != null
460 && (!Application.isPlaying || _temporalWarpingMode == TemporalWarpingMode.Off)) {
461 // Transform mode at edit-time -- just use the transform pose.
462 currentPose = deviceOrigin.ToPose();
463 } else if (!Application.isPlaying) {
464 currentPose.position =
465 currentPose.rotation * Vector3.up * deviceOffsetYAxis
466 + currentPose.rotation * Vector3.forward * deviceOffsetZAxis;
467 currentPose.rotation = Quaternion.Euler(deviceTiltXAxis, 0f, 0f);
468 currentPose = transform.ToLocalPose().Then(currentPose);
469 } else {
470 transformHistory.SampleTransform(timestamp, out currentPose.position,
471 out currentPose.rotation);
472 }
473
474 // Choose between Warped and Current Pose
475 bool useCurrentPosition =
476 _temporalWarpingMode == TemporalWarpingMode.Off ||
477 !Application.isPlaying;
478 warpedPosition = useCurrentPosition ? currentPose.position : warpedPosition;
479 warpedRotation = useCurrentPosition ? currentPose.rotation : warpedRotation;
480
481 // Apply offsets (when applicable)
482 if (Application.isPlaying) {
483 if (_deviceOffsetMode != DeviceOffsetMode.Transform) {
485 + warpedRotation * Vector3.forward * deviceOffsetZAxis;
486 warpedRotation *= Quaternion.Euler(deviceTiltXAxis, 0f, 0f);
487
488 warpedRotation *= Quaternion.Euler(-90f, 180f, 0f);
489 } else {
490 warpedRotation *= Quaternion.Euler(-90f, 90f, 90f);
491 }
492 }
493
494 if (this == null) {
495 // We are being destroyed, get outta here.
496 return LeapTransform.Identity;
497 }
498 if (transform.parent != null
499 && _deviceOffsetMode != DeviceOffsetMode.Transform) {
500 leapTransform = new LeapTransform(
501 transform.parent.TransformPoint(warpedPosition).ToVector(),
502 (transform.parent.rotation * warpedRotation).ToLeapQuaternion(),
503 transform.lossyScale.ToVector() * 1e-3f
504 );
505 } else {
506 leapTransform = new LeapTransform(
507 warpedPosition.ToVector(),
508 warpedRotation.ToLeapQuaternion(),
509 transform.lossyScale.ToVector() * 1e-3f
510 );
511 }
512
513 leapTransform.MirrorZ();
514
515 return leapTransform;
516 }
517
518 protected void transformHands(ref LeapTransform LeftHand, ref LeapTransform RightHand) {
519 LeapTransform leapTransform = GetWarpedMatrix(0, false);
520 LeftHand = new LeapTransform(leapTransform.TransformPoint(LeftHand.translation),
521 leapTransform.TransformQuaternion(LeftHand.rotation));
522 RightHand = new LeapTransform(leapTransform.TransformPoint(RightHand.translation),
523 leapTransform.TransformQuaternion(RightHand.rotation));
524 }
525
526 protected void OnPreCullHandTransforms(Camera camera) {
528 //Don't update pre cull for preview, reflection, or scene view cameras
529 if (camera == null) camera = preCullCamera;
530 switch (camera.cameraType) {
531 case CameraType.Preview:
532#if UNITY_2017_1_OR_NEWER
533 case CameraType.Reflection:
534#endif
535 case CameraType.SceneView:
536 return;
537 }
538
539 if (Application.isPlaying
541 && _leapController != null) {
543
544 //Find the left and/or right hand(s) to latch
545 Hand leftHand = null, rightHand = null;
546 LeapTransform precullLeftHand = LeapTransform.Identity;
547 LeapTransform precullRightHand = LeapTransform.Identity;
548 for (int i = 0; i < CurrentFrame.Hands.Count; i++) {
549 Hand updateHand = CurrentFrame.Hands[i];
550 if (updateHand.IsLeft && leftHand == null) {
551 leftHand = updateHand;
552 } else if (updateHand.IsRight && rightHand == null) {
553 rightHand = updateHand;
554 }
555 }
556
557 //Determine their new Transforms
558 var interpolationTime = CalculateInterpolationTime();
560 interpolationTime + (ExtrapolationAmount * 1000),
561 interpolationTime - (BounceAmount * 1000),
562 (leftHand != null ? leftHand.Id : 0),
563 (rightHand != null ? rightHand.Id : 0),
564 out precullLeftHand,
565 out precullRightHand);
566 bool leftValid = precullLeftHand.translation != Vector.Zero;
567 bool rightValid = precullRightHand.translation != Vector.Zero;
568 transformHands(ref precullLeftHand, ref precullRightHand);
569
570 //Calculate the delta Transforms
571 if (rightHand != null && rightValid) {
572 _transformArray[0] =
573 Matrix4x4.TRS(precullRightHand.translation.ToVector3(),
574 precullRightHand.rotation.ToQuaternion(),
575 Vector3.one)
576 * Matrix4x4.Inverse(Matrix4x4.TRS(rightHand.PalmPosition.ToVector3(),
577 rightHand.Rotation.ToQuaternion(),
578 Vector3.one));
579 }
580 if (leftHand != null && leftValid) {
581 _transformArray[1] =
582 Matrix4x4.TRS(precullLeftHand.translation.ToVector3(),
583 precullLeftHand.rotation.ToQuaternion(),
584 Vector3.one)
585 * Matrix4x4.Inverse(Matrix4x4.TRS(leftHand.PalmPosition.ToVector3(),
586 leftHand.Rotation.ToQuaternion(),
587 Vector3.one));
588 }
589
590 //Apply inside of the vertex shader
591 Shader.SetGlobalMatrixArray(HAND_ARRAY_GLOBAL_NAME, _transformArray);
592 }
593 }
594 }
595
596 #endregion
597
598 }
599
600}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
The Controller class is your main interface to the Leap Motion Controller.
void GetInterpolatedLeftRightTransform(Int64 time, Int64 sourceTime, int leftId, int rightId, out LeapTransform leftTransform, out LeapTransform rightTransform)
This is a special variant of GetInterpolatedFrameFromTime, for use with special features that only re...
long FrameTimestamp(int history=0)
Returns the timestamp of a recent tracking frame. Use the optional history parameter to specify how m...
long Now()
Returns a timestamp value as close as possible to the current time. Values are in microseconds,...
void ClearPolicy(PolicyFlag policy)
Requests clearing a policy.
void SetPolicy(PolicyFlag policy)
Requests setting a policy.
The Frame class represents a set of hand and finger tracking data detected in a single frame.
Definition: Frame.cs:24
long Timestamp
The frame capture time in microseconds elapsed since an arbitrary point in time in the past.
Definition: Frame.cs:136
List< Hand > Hands
The list of Hand objects detected in this frame, given in arbitrary order. The list can be empty if n...
Definition: Frame.cs:156
The Hand class reports the physical characteristics of a detected hand.
Definition: Hand.cs:26
bool IsLeft
Identifies whether this Hand is a left hand.
Definition: Hand.cs:296
Vector PalmPosition
The center position of the palm.
Definition: Hand.cs:165
int Id
A unique ID assigned to this Hand object, whose value remains the same across consecutive frames whil...
Definition: Hand.cs:152
LeapQuaternion Rotation
The rotation of the hand as a quaternion.
Definition: Hand.cs:211
bool IsRight
Identifies whether this Hand is a right hand.
Definition: Hand.cs:302
TestHandPose editTimePose
Definition: LeapProvider.cs:23
The LeapServiceProvider provides tracked Leap Hand data and images from the device via the Leap servi...
const double S_TO_NS
Converts seconds to nanoseconds.
const string HAND_ARRAY_GLOBAL_NAME
The transform array used for late-latching.
The LeapXRServiceProvider expands on the standard LeapServiceProvider to account for the offset of th...
virtual void onPreCull(Camera preCullingCamera)
override void transformFrame(Frame source, Frame dest)
virtual LeapTransform GetWarpedMatrix(long timestamp, bool updateTemporalCompensation=true)
void resetShaderTransforms()
Resets shader globals for the Hand transforms.
override long CalculateInterpolationTime(bool endOfFrame=false)
void transformHands(ref LeapTransform LeftHand, ref LeapTransform RightHand)
override void initializeFlags()
Initializes the Leap Motion policy flags. The POLICY_OPTIMIZE_HMD flag improves tracking for head-mou...
Implements a resample-able transform history.
void SampleTransform(long timestamp, out Vector3 delayedPos, out Quaternion delayedRot)
RingBuffer< TransformData > history
void UpdateDelay(Pose curPose, long timestamp)
The LeapTransform class represents a transform in three dimensional space.
Vector TransformPoint(Vector point)
Transforms the specified position vector, applying translation, rotation and scale.
static readonly LeapTransform Identity
The identity transform.
LeapQuaternion rotation
The rotational component of the transform.
void MirrorZ()
Mirrors this transform's rotation and scale across the z-axis. Translation is not affected.
Vector translation
The translation component of the transform.
LeapQuaternion TransformQuaternion(LeapQuaternion rhs)
Transforms the specified quaternion. Multiplies the quaternion representing the rotational part of th...
A position and rotation. You can multiply two poses; this acts like Matrix4x4 multiplication,...
Definition: Pose.cs:21
Pose inverse
Definition: Pose.cs:37
Quaternion rotation
Definition: Pose.cs:24
static readonly Pose identity
Definition: Pose.cs:35
Vector3 position
Definition: Pose.cs:23
The Vector struct represents a three-component mathematical vector or point such as a direction or po...
Definition: Vector.cs:36
static readonly Vector Zero
The zero vector: (0, 0, 0)
Definition: Vector.cs:313