Tanoda
TimelinePostProcess.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.Linq;
10using System.Collections.Generic;
11using UnityEngine;
12using UnityEngine.Timeline;
13#if UNITY_EDITOR
14using UnityEditor;
15#endif
16
17namespace Leap.Unity.Recording {
18 using Query;
19
20 public class TimelinePostProcess : MonoBehaviour {
21
22 public TimelineAsset[] assets = new TimelineAsset[0];
23 public string headPositionPath = "Leap Rig/Main Camera";
24
25 public bool lerpStartingPosition = true;
27 public bool lerpEndingPosition = true;
29
30 public bool cropAnimation = false;
31
32 public string[] allBindings;
33
34#if UNITY_EDITOR
35 private void OnValidate() {
36 allBindings = allClips.SelectMany(c => AnimationUtility.GetCurveBindings(c.Value)).
37 Select(b => b.path).
38 Distinct().
39 OrderBy(p => p).
40 ToArray();
41 }
42
43 [ContextMenu("Perform Post Process")]
44 public void PerformPostProcess() {
45 Dictionary<AnimationClip, TimeRange> ranges = new Dictionary<AnimationClip, TimeRange>();
46
47 float total = allClips.Count();
48 int index = 0;
49
50 foreach (var pair in allClips) {
51 var clip = pair.Key;
52 var animClip = pair.Value;
53
54 EditorUtility.DisplayCancelableProgressBar("Post processing clips", "Processing " + animClip.name, index / total);
55
56 TimeRange range;
57 if (!ranges.TryGetValue(animClip, out range)) {
58 range = new TimeRange() {
59 start = (float)clip.clipIn,
60 end = (float)(clip.clipIn + clip.duration)
61 };
62 } else {
63 range.start = Mathf.Min(range.start, (float)clip.clipIn);
64 range.end = Mathf.Max(range.end, (float)(clip.clipIn + clip.duration));
65 }
66
67 ranges[animClip] = range;
68 }
69
70 try {
71 foreach (var pair in allClips) {
72 var clip = pair.Key;
73 var animClip = pair.Value;
74
75 index++;
76 EditorUtility.DisplayCancelableProgressBar("Post-processing clips", "Processing " + animClip.name, index / total);
77
78 BlendHeadPosition(clip, animClip);
79
80 if (cropAnimation) {
81 CropAnimation(animClip, (float)clip.clipIn, (float)(clip.clipIn + clip.duration));
82 }
83 }
84
85 AssetDatabase.Refresh();
86 AssetDatabase.SaveAssets();
87 } finally {
88 EditorUtility.ClearProgressBar();
89 }
90 }
91
92 public void CropAnimation(AnimationClip clip, float start, float end) {
93 var bindings = AnimationUtility.GetCurveBindings(clip);
94
95 foreach (var binding in bindings) {
96 var curve = AnimationUtility.GetEditorCurve(clip, binding);
97 var cropped = AnimationCurveUtil.GetCropped(curve, start, end, slideToStart: true);
98 AnimationUtility.SetEditorCurve(clip, binding, cropped);
99 }
100 }
101
102 public void BlendHeadPosition(TimelineClip clip, AnimationClip animClip) {
103 var bindings = AnimationUtility.GetCurveBindings(animClip);
104
105 float startTime = (float)clip.clipIn;
106 float endTime = (float)(clip.clipIn + clip.duration);
107
108 var xBinding = bindings.Query().FirstOrNone(b => b.path == headPositionPath && b.propertyName == "m_LocalPosition.x");
109 var yBinding = bindings.Query().FirstOrNone(b => b.path == headPositionPath && b.propertyName == "m_LocalPosition.y");
110 var zBinding = bindings.Query().FirstOrNone(b => b.path == headPositionPath && b.propertyName == "m_LocalPosition.z");
111
112 if (!xBinding.hasValue || !yBinding.hasValue || !zBinding.hasValue) {
113 return;
114 }
115
116 xBinding.Match(xB => {
117 yBinding.Match(yB => {
118 zBinding.Match(zB => {
119 bindings = new EditorCurveBinding[] { xB, yB, zB };
120 });
121 });
122 });
123
124 for (int i = 0; i < 3; i++) {
125 var binding = bindings[i];
126 var curve = AnimationUtility.GetEditorCurve(animClip, binding);
127
128 float startPos = curve.Evaluate(startTime);
129 float endPos = curve.Evaluate(endTime);
130
131 float startOffset = startHeadPosition[i] - startPos;
132 float endOffset = endHeadPosition[i] - endPos;
133
134 if (!lerpStartingPosition) startOffset = 0;
135 if (!lerpEndingPosition) endOffset = 0;
136
137 var keys = curve.keys;
138 for (int j = 0; j < keys.Length; j++) {
139 var key = keys[j];
140
141 float percent = (key.time - startTime) / (endTime - startTime);
142 float offset = Mathf.LerpUnclamped(startOffset, endOffset, percent);
143 key.value += offset;
144
145 curve.MoveKey(j, key);
146 }
147
148 AnimationUtility.SetEditorCurve(animClip, binding, curve);
149 }
150 }
151
152 private IEnumerable<KeyValuePair<TimelineClip, AnimationClip>> allClips {
153 get {
154 foreach (var timeline in assets) {
155 if (timeline == null) continue;
156
157 foreach (var track in timeline.GetOutputTracks()) {
158 var animTrack = track as AnimationTrack;
159 if (animTrack != null) {
160 foreach (var clip in animTrack.GetClips()) {
161 var animClip = clip.animationClip;
162 yield return new KeyValuePair<TimelineClip, AnimationClip>(clip, animClip);
163 }
164 }
165 }
166 }
167 }
168 }
169
170 private struct TimeRange {
171 public float start, end;
172 }
173#endif
174 }
175}
Vector3 startHeadPosition
Vector3 endHeadPosition
bool cropAnimation
TimelineAsset[] assets
bool lerpStartingPosition
string[] allBindings
bool lerpEndingPosition
string headPositionPath