Tanoda
LeapServiceProvider.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 System;
10using System.Collections.Generic;
11using UnityEngine;
12
13namespace Leap.Unity {
14 using Attributes;
15
21
22 #region Constants
23
27 protected const double NS_TO_S = 1e-6;
28
32 protected const double S_TO_NS = 1e6;
33
37 protected const string HAND_ARRAY_GLOBAL_NAME = "_LeapHandTransforms";
38
43 protected const int MAX_RECONNECTION_ATTEMPTS = 5;
44
49 protected const int RECONNECTION_INTERVAL = 180;
50
51 #endregion
52
53 #region Inspector
54
56 None,
57 LeapMotionController,
58 StereoIR170,
59 Automatic
60 }
61 [Tooltip("Displays a representation of the interaction volume in the scene view")]
62 [SerializeField]
64
66
68 None,
69 ReuseUpdateForPhysics,
70 ReusePhysicsForUpdate,
71 }
72 [Tooltip("When enabled, the provider will only calculate one leap frame instead of two.")]
73 [SerializeField]
75
77 None,
78 Auto,
79 Manual
80 }
81 [Tooltip("The mode to use when extrapolating physics.\n" +
82 " None - No extrapolation is used at all.\n" +
83 " Auto - Extrapolation is chosen based on the fixed timestep.\n" +
84 " Manual - Extrapolation time is chosen manually by the user.")]
85 [SerializeField]
87
88 [Tooltip("The amount of time (in seconds) to extrapolate the physics data by.")]
89 [SerializeField]
90 protected float _physicsExtrapolationTime = 1.0f / 90.0f;
91
93 Desktop,
94 ScreenTop,
95 HMD
96 }
97 [Tooltip("[Service must be >= 4.9.2!] " +
98 "Which tracking mode to request that the service optimize for. " +
99 "(Use the LeapXRServiceProvider for HMD Mode instead of this option!)")]
100 [SerializeField]
103
104#if UNITY_2017_3_OR_NEWER
105 [Tooltip("When checked, profiling data from the LeapCSharp worker thread will be used to populate the UnityProfiler.")]
107#else
108 [Tooltip("Worker thread profiling requires a Unity version of 2017.3 or greater.")]
109 [Disable]
110#endif
111 [SerializeField]
112 protected bool _workerThreadProfiling = false;
113
114 [Tooltip("Which Leap Service API Endpoint to connect to. This is configured on the service with the 'api_namespace' argument.")]
115 [SerializeField]
117 protected string _serverNameSpace = "Leap Service";
118
119 #endregion
120
121 #region Internal Settings & Memory
122 protected bool _useInterpolation = true;
123
124 // Extrapolate on Android to compensate for the latency introduced by its graphics
125 // pipeline.
126#if UNITY_ANDROID && !UNITY_EDITOR
127 protected int ExtrapolationAmount = 15;
128 protected int BounceAmount = 70;
129#else
130 protected int ExtrapolationAmount = 0;
131 protected int BounceAmount = 0;
132#endif
133
135 protected bool _isDestroyed;
136
139 protected long _unityToLeapOffset;
140
145
146 #endregion
147
148 #region Edit-time Frame Data
149
150 private Action<Device> _onDeviceSafe;
158 public event Action<Device> OnDeviceSafe {
159 add {
161 foreach (var device in _leapController.Devices) {
162 value(device);
163 }
164 }
165 _onDeviceSafe += value;
166 }
167 remove {
168 _onDeviceSafe -= value;
169 }
170 }
171
172 #if UNITY_EDITOR
173 private Frame _backingUntransformedEditTimeFrame = null;
174 private Frame _untransformedEditTimeFrame {
175 get {
176 if (_backingUntransformedEditTimeFrame == null) {
177 _backingUntransformedEditTimeFrame = new Frame();
178 }
179 return _backingUntransformedEditTimeFrame;
180 }
181 }
182 private Frame _backingEditTimeFrame = null;
183 private Frame _editTimeFrame {
184 get {
185 if (_backingEditTimeFrame == null) {
186 _backingEditTimeFrame = new Frame();
187 }
188 return _backingEditTimeFrame;
189 }
190 }
191
192 private Dictionary<TestHandFactory.TestHandPose, Hand> _cachedLeftHands
193 = new Dictionary<TestHandFactory.TestHandPose, Hand>();
194 private Hand _editTimeLeftHand {
195 get {
196 Hand cachedHand = null;
197 if (_cachedLeftHands.TryGetValue(editTimePose, out cachedHand)) {
198 return cachedHand;
199 }
200 else {
201 cachedHand = TestHandFactory.MakeTestHand(isLeft: true, pose: editTimePose);
202 _cachedLeftHands[editTimePose] = cachedHand;
203 return cachedHand;
204 }
205 }
206 }
207
208 private Dictionary<TestHandFactory.TestHandPose, Hand> _cachedRightHands
209 = new Dictionary<TestHandFactory.TestHandPose, Hand>();
210 private Hand _editTimeRightHand {
211 get {
212 Hand cachedHand = null;
213 if (_cachedRightHands.TryGetValue(editTimePose, out cachedHand)) {
214 return cachedHand;
215 }
216 else {
217 cachedHand = TestHandFactory.MakeTestHand(isLeft: false, pose: editTimePose);
218 _cachedRightHands[editTimePose] = cachedHand;
219 return cachedHand;
220 }
221 }
222 }
223
224 #endif
225
226 #endregion
227
228 #region LeapProvider Implementation
229
230 public override Frame CurrentFrame {
231 get {
232 #if UNITY_EDITOR
233 if (!Application.isPlaying) {
234 _editTimeFrame.Hands.Clear();
235 _untransformedEditTimeFrame.Hands.Clear();
236 _untransformedEditTimeFrame.Hands.Add(_editTimeLeftHand);
237 _untransformedEditTimeFrame.Hands.Add(_editTimeRightHand);
238 transformFrame(_untransformedEditTimeFrame, _editTimeFrame);
239 return _editTimeFrame;
240 }
241 #endif
242 if (_frameOptimization == FrameOptimizationMode.ReusePhysicsForUpdate) {
244 } else {
246 }
247 }
248 }
249
250 public override Frame CurrentFixedFrame {
251 get {
252 #if UNITY_EDITOR
253 if (!Application.isPlaying) {
254 _editTimeFrame.Hands.Clear();
255 _untransformedEditTimeFrame.Hands.Clear();
256 _untransformedEditTimeFrame.Hands.Add(_editTimeLeftHand);
257 _untransformedEditTimeFrame.Hands.Add(_editTimeRightHand);
258 transformFrame(_untransformedEditTimeFrame, _editTimeFrame);
259 return _editTimeFrame;
260 }
261 #endif
262 if (_frameOptimization == FrameOptimizationMode.ReuseUpdateForPhysics) {
264 } else {
266 }
267 }
268 }
269
270 #endregion
271
272 #region Unity Events
273
274 protected virtual void Reset() {
275 editTimePose = TestHandFactory.TestHandPose.DesktopModeA;
276 }
277
278 protected virtual void Awake() {
279 _fixedOffset.delay = 0.4f;
280 _smoothedTrackingLatency.SetBlend(0.99f, 0.0111f);
281 }
282
283 protected virtual void Start() {
289 }
290
291 protected virtual void Update() {
293 LeapProfiling.Update();
294 }
295
296 if (!checkConnectionIntegrity()) { return; }
297
298#if UNITY_EDITOR
299 if (UnityEditor.EditorApplication.isCompiling) {
300 UnityEditor.EditorApplication.isPlaying = false;
301 Debug.LogWarning("Unity hot reloading not currently supported. Stopping Editor Playback.");
302 return;
303 }
304#endif
305
306 _fixedOffset.Update(Time.time - Time.fixedTime, Time.deltaTime);
307
308 if (_frameOptimization == FrameOptimizationMode.ReusePhysicsForUpdate) {
310 return;
311 }
312
313 if (_useInterpolation) {
314#if !UNITY_ANDROID || UNITY_EDITOR
317#endif
318 long timestamp = CalculateInterpolationTime() + (ExtrapolationAmount * 1000);
319 _unityToLeapOffset = timestamp - (long)(Time.time * S_TO_NS);
320
322 }
323 else {
325 }
326
327 if (_untransformedUpdateFrame != null) {
329
331 }
332 }
333
334 protected virtual void FixedUpdate() {
335 if (_frameOptimization == FrameOptimizationMode.ReuseUpdateForPhysics) {
337 return;
338 }
339
340 if (_useInterpolation) {
341
342 long timestamp;
343 switch (_frameOptimization) {
344 case FrameOptimizationMode.None:
345 // By default we use Time.fixedTime to ensure that our hands are on the same
346 // timeline as Update. We add an extrapolation value to help compensate
347 // for latency.
348 float extrapolatedTime = Time.fixedTime + CalculatePhysicsExtrapolation();
349 timestamp = (long)(extrapolatedTime * S_TO_NS) + _unityToLeapOffset;
350 break;
351 case FrameOptimizationMode.ReusePhysicsForUpdate:
352 // If we are re-using physics frames for update, we don't even want to care
353 // about Time.fixedTime, just grab the most recent interpolated timestamp
354 // like we are in Update.
355 timestamp = CalculateInterpolationTime() + (ExtrapolationAmount * 1000);
356 break;
357 default:
358 throw new System.InvalidOperationException(
359 "Unexpected frame optimization mode: " + _frameOptimization);
360 }
362
363 }
364 else {
366 }
367
368 if (_untransformedFixedFrame != null) {
370
372 }
373 }
374
375 protected virtual void OnDestroy() {
377 _isDestroyed = true;
378 }
379
380 protected virtual void OnApplicationPause(bool isPaused) {
381 if (_leapController != null) {
382 if (isPaused) {
384 }
385 else {
387 }
388 }
389 }
390
391 protected virtual void OnApplicationQuit() {
393 _isDestroyed = true;
394 }
395
397 switch (_physicsExtrapolation) {
398 case PhysicsExtrapolationMode.None:
399 return 0;
400 case PhysicsExtrapolationMode.Auto:
401 return Time.fixedDeltaTime;
402 case PhysicsExtrapolationMode.Manual:
404 default:
405 throw new System.InvalidOperationException(
406 "Unexpected physics extrapolation mode: " + _physicsExtrapolation);
407 }
408 }
409
410 #endregion
411
412 #region Public API
413
418 #if UNITY_EDITOR
419 // Null check to deal with hot reloading.
420 if (!_isDestroyed && _leapController == null) {
422 }
423 #endif
424 return _leapController;
425 }
426
431 public bool IsConnected() {
433 }
434
440 public void RetransformFrames() {
443 }
444
451 LeapXRServiceProvider leapXRServiceProvider) {
453 leapXRServiceProvider._frameOptimization = _frameOptimization;
454 leapXRServiceProvider._physicsExtrapolation = _physicsExtrapolation;
456 leapXRServiceProvider._workerThreadProfiling = _workerThreadProfiling;
457 }
458
459 #endregion
460
461 #region Internal Methods
462
463 protected virtual long CalculateInterpolationTime(bool endOfFrame = false) {
464 #if UNITY_ANDROID && !UNITY_EDITOR
465 return _leapController.Now() - 16000;
466 #else
467 if (_leapController != null) {
469 } else {
470 return 0;
471 }
472 #endif
473 }
474
478 protected virtual void initializeFlags() {
479 if (_leapController == null) {
480 return;
481 }
482
485 _leapController.ClearPolicy(Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP);
487 } else if (_trackingOptimization == TrackingOptimizationMode.ScreenTop) {
490 _leapController.SetPolicy (Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP);
493 _leapController.ClearPolicy(Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP);
494 _leapController.SetPolicy (Controller.PolicyFlag.POLICY_OPTIMIZE_HMD);
495 }
496 }
497
502 protected void createController() {
503 if (_leapController != null) {
504 return;
505 }
506
508 _leapController.Device += (s, e) => {
509 if (_onDeviceSafe != null) {
510 _onDeviceSafe(e.Device);
511 }
512 };
513
516 } else {
518 }
519
521 //A controller will report profiling statistics for the duration of it's lifetime
522 //so these events will never be unsubscribed from.
523 _leapController.EndProfilingBlock += LeapProfiling.EndProfilingBlock;
524 _leapController.BeginProfilingBlock += LeapProfiling.BeginProfilingBlock;
525
526 _leapController.EndProfilingForThread += LeapProfiling.EndProfilingForThread;
527 _leapController.BeginProfilingForThread += LeapProfiling.BeginProfilingForThread;
528 }
529 }
530
535 protected void destroyController() {
536 if (_leapController != null) {
538 _leapController.ClearPolicy(Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP);
540 }
543 _leapController = null;
544 }
545 }
546
547 private int _framesSinceServiceConnectionChecked = 0;
548 private int _numberOfReconnectionAttempts = 0;
554 protected bool checkConnectionIntegrity() {
556 _framesSinceServiceConnectionChecked = 0;
557 _numberOfReconnectionAttempts = 0;
558 return true;
559 } else if (_numberOfReconnectionAttempts < MAX_RECONNECTION_ATTEMPTS) {
560 _framesSinceServiceConnectionChecked++;
561
562 if (_framesSinceServiceConnectionChecked > RECONNECTION_INTERVAL) {
563 _framesSinceServiceConnectionChecked = 0;
564 _numberOfReconnectionAttempts++;
565
566 Debug.LogWarning("Leap Service not connected; attempting to reconnect for try " +
567 _numberOfReconnectionAttempts + "/" + MAX_RECONNECTION_ATTEMPTS +
568 "...", this);
569 using (new ProfilerSample("Reconnection Attempt")) {
572 }
573 }
574 }
575 return false;
576 }
577
578 protected void onHandControllerConnect(object sender, LeapEventArgs args) {
580
581 if (_leapController != null) {
583 }
584 }
585
586 protected virtual void transformFrame(Frame source, Frame dest) {
587 dest.CopyFrom(source).Transform(transform.GetLeapMatrix());
588 }
589
590 #endregion
591
592 }
593
594}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
The Controller class is your main interface to the Leap Motion Controller.
Action< EndProfilingForThreadArgs > EndProfilingForThread
Dispatched whenever a thread is finished profiling. The event is always dispatched from the thread it...
long FrameTimestamp(int history=0)
Returns the timestamp of a recent tracking frame. Use the optional history parameter to specify how m...
Action< EndProfilingBlockArgs > EndProfilingBlock
Dispatched whenever a thread ends a profiling block. The event is always dispatched from the thread i...
Action< BeginProfilingForThreadArgs > BeginProfilingForThread
Dispatched whenever a thread wants to start profiling for a custom thread. The event is always dispat...
long Now()
Returns a timestamp value as close as possible to the current time. Values are in microseconds,...
Action< BeginProfilingBlockArgs > BeginProfilingBlock
Dispatched whenever a thread enters a profiling block. The event is always dispatched from the thread...
void ClearPolicy(PolicyFlag policy)
Requests clearing a policy.
void SetPolicy(PolicyFlag policy)
Requests setting a policy.
bool IsServiceConnected
Reports whether your application has a connection to the Leap Motion daemon/service....
DeviceList Devices
The list of currently attached and recognized Leap Motion controller devices.
Frame GetInterpolatedFrame(Int64 time)
Returns the Frame at the specified time, interpolating the data between existing frames,...
bool IsConnected
Reports whether this Controller is connected to the Leap Motion service and the Leap Motion hardware ...
Frame Frame(int history=0)
In most cases you should get Frame objects using the LeapProvider.CurrentFrame property....
EventHandler< DeviceEventArgs > Device
Dispatched when a Leap Motion device is connected.
void GetInterpolatedFrameFromTime(Frame toFill, Int64 time, Int64 sourceTime)
The Frame class represents a set of hand and finger tracking data detected in a single frame.
Definition: Frame.cs:24
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
A generic object with no arguments beyond the event type.
Definition: Events.cs:42
Provides Frame object data to the Unity application by firing events as soon as Frame data is availab...
Definition: LeapProvider.cs:21
void DispatchUpdateFrameEvent(Frame frame)
Definition: LeapProvider.cs:49
TestHandPose editTimePose
Definition: LeapProvider.cs:23
void DispatchFixedFrameEvent(Frame frame)
Definition: LeapProvider.cs:58
The LeapServiceProvider provides tracked Leap Hand data and images from the device via the Leap servi...
virtual void transformFrame(Frame source, Frame dest)
const double S_TO_NS
Converts seconds to nanoseconds.
InteractionVolumeVisualization _interactionVolumeVisualization
virtual void OnApplicationPause(bool isPaused)
Action< Device > OnDeviceSafe
A utility event to get a callback whenever a new device is connected to the service....
const int RECONNECTION_INTERVAL
The number of frames to wait between each reconnection attempt.
virtual long CalculateInterpolationTime(bool endOfFrame=false)
FrameOptimizationMode _frameOptimization
void RetransformFrames()
Retransforms hand data from Leap space to the space of the Unity transform. This is only necessary if...
Controller GetLeapController()
Returns the Leap Controller instance.
const double NS_TO_S
Converts nanoseconds to seconds.
void onHandControllerConnect(object sender, LeapEventArgs args)
void destroyController()
Stops the connection for the existing instance of a Controller, clearing old policy flags and resetti...
virtual void initializeFlags()
Initializes Leap Motion policy flags.
void createController()
Creates an instance of a Controller, initializing its policy flags and subscribing to its connection ...
PhysicsExtrapolationMode _physicsExtrapolation
TrackingOptimizationMode _trackingOptimization
InteractionVolumeVisualization SelectedInteractionVolumeVisualization
const string HAND_ARRAY_GLOBAL_NAME
The transform array used for late-latching.
void CopySettingsToLeapXRServiceProvider(LeapXRServiceProvider leapXRServiceProvider)
Copies property settings from this LeapServiceProvider to the target LeapXRServiceProvider where appl...
bool IsConnected()
Returns true if the Leap Motion hardware is plugged in and this application is connected to the Leap ...
bool checkConnectionIntegrity()
Checks whether this provider is connected to a service; If it is not, attempt to reconnect at regular...
const int MAX_RECONNECTION_ATTEMPTS
The maximum number of times the provider will attempt to reconnect to the service before giving up.
The LeapXRServiceProvider expands on the standard LeapServiceProvider to account for the offset of th...
Time-step independent exponential smoothing.
void SetBlend(float blend, float deltaTime=1f)
float Update(float input, float deltaTime=1f)
A utility struct for ease of use when you want to wrap a piece of code in a Profiler....