Tanoda
InertiaPostProcessProvider.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 Leap.Unity.Query;
10using UnityEngine;
11
13
15
16 [Header("Inertia")]
17
18 [Tooltip("Higher stiffness will keep the bouncy hand closer to the tracked hand data.")]
19 [Range(0f, 10f)]
20 public float stiffness = 2f;
21
22 [Tooltip("Higher damping will suppress more motion and reduce oscillation.")]
23 [Range(0f, 10f)]
24 public float damping = 2f;
25
26 // Update-time Hand Data
27 private Pose? _leftPose = null;
28 private Pose? _previousLeftPose = null;
29 private float _leftAge = 0f;
30 private Pose? _rightPose = null;
31 private Pose? _previousRightPose = null;
32 private float _rightAge = 0f;
33
34 // FixedUpdate-time Hand Data
35 private Pose? _fixedLeftPose = null;
36 private Pose? _fixedPreviousLeftPose = null;
37 private float _fixedLeftAge = 0f;
38 private Pose? _fixedRightPose = null;
39 private Pose? _fixedPreviousRightPose = null;
40 private float _fixedRightAge = 0f;
41
45 public override void ProcessFrame(ref Frame inputFrame) {
46 var leftHand = inputFrame.Hands.Query().FirstOrDefault(h => h.IsLeft);
47 var rightHand = inputFrame.Hands.Query().FirstOrDefault(h => !h.IsLeft);
48
49 // Frames can potentially come from two time-interwoven sources: Update frames
50 // and FixedUpdate frames. Time is not monotonically increasing frame-to-frame
51 // because FixedUpdates and Updates interweave and occasionally FixedUpdate plays
52 // catch-up, and we interpolate hand data accordingly further up the hand pipeline,
53 // which affects the hand.TimeVisible property we use to simulate our effect
54 // statefully over time.
55 //
56 // To support both Update-time hand data and FixedUpdate-time hand data with a
57 // single stateful post-process, we maintain two independent states for each stream,
58 // which, independently, _are_ going to be monotonically forward-moving in time.
59 if (Time.inFixedTimeStep) {
60 // FixedUpdate hand data.
61 processHand(leftHand,
62 ref _fixedLeftPose, ref _fixedPreviousLeftPose, ref _fixedLeftAge);
63 processHand(rightHand,
64 ref _fixedRightPose, ref _fixedPreviousRightPose, ref _fixedRightAge);
65 }
66 else {
67 // Update hand data.
68 processHand(leftHand, ref _leftPose, ref _previousLeftPose, ref _leftAge);
69 processHand(rightHand, ref _rightPose, ref _previousRightPose, ref _rightAge);
70 }
71
72 }
73
74 private void processHand(Hand hand,
75 ref Pose? maybeCurPose,
76 ref Pose? maybePrevPose,
77 ref float handAge) {
78 if (hand == null) {
79 // Clear state.
80 maybeCurPose = null;
81 maybePrevPose = null;
82 handAge = 0f;
83 }
84 else {
85 var framePose = hand.GetPalmPose();
86
87 if (!maybeCurPose.HasValue) {
88 // The hand just started being tracked.
89 maybePrevPose = null;
90 maybeCurPose = framePose;
91 }
92 else if (!maybePrevPose.HasValue) {
93 // Have current pose, lack previous pose, just get initial momentum.
94 maybePrevPose = maybeCurPose;
95 maybeCurPose = framePose;
96 }
97 else {
98 // There's enough data to verlet-integrate.
99
100 // Calculate how much time has passed since we last received hand data.
101 //
102 // As a safety measure, we ensure deltaTime is positive before running our
103 // stateful filter to give the hand momentum. Any post-process could mess with
104 // the TimeVisible property, so we do this to minimize the chance of total
105 // havok.
106 var deltaTime = hand.TimeVisible - handAge;
107 if (deltaTime > 0) {
108 handAge = hand.TimeVisible;
109
110 var curPose = maybeCurPose.Value;
111 var prevPose = maybePrevPose.Value;
112 integratePose(ref curPose, ref prevPose,
113 targetPose: framePose, deltaTime: deltaTime);
114 hand.SetPalmPose(curPose);
115 maybeCurPose = curPose;
116 maybePrevPose = prevPose;
117 }
118 }
119 }
120 }
121
126 private void integratePose(ref Pose curPose, ref Pose prevPose,
127 Pose targetPose, float deltaTime) {
128 // Calculate motion from prevPose to curPose.
129 var deltaPose = curPose.inverse * prevPose; // prevPose in curPose's local space.
130 deltaPose = new Pose(-deltaPose.position, Quaternion.Inverse(deltaPose.rotation));
131 deltaPose = Pose.Lerp(deltaPose, Pose.identity, damping * deltaTime); // Dampen.
132
133 // Verlet-integrate curPose based on the delta from prevPose.
134 Pose tempPose = curPose;
135 curPose = curPose * deltaPose;
136 prevPose = tempPose;
137
138 // Pull the integrated hand toward the target a little bit based on stiffness.
139 curPose = Pose.Lerp(curPose, targetPose, stiffness * deltaTime);
140 }
141 }
142}
The Frame class represents a set of hand and finger tracking data detected in a single frame.
Definition: Frame.cs:24
The Hand class reports the physical characteristics of a detected hand.
Definition: Hand.cs:26
float TimeVisible
The duration of time this Hand has been visible to the Leap Motion Controller.
Definition: Hand.cs:282
float damping
float stiffness
override void ProcessFrame(ref Frame inputFrame)
Post-processes the input frame in place to give hands bouncy-feeling physics.
A position and rotation. You can multiply two poses; this acts like Matrix4x4 multiplication,...
Definition: Pose.cs:21