Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
AnimationChannel.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using SiliconStudio.Core.Collections;
6 using SiliconStudio.Core.Extensions;
7 
8 namespace SiliconStudio.Paradox.DataModel
9 {
10  /// <summary>
11  /// List of float key frame data applying to a specific property in a node.
12  /// </summary>
13  public class AnimationChannel
14  {
16  {
17  KeyFrames = new List<KeyFrameData<float>>();
18  }
19 
20  public float EvaluateCubic(CompressedTimeSpan time)
21  {
22  int keyIndex;
23  for (keyIndex = 0; keyIndex < KeyFrames.Count - 1; ++keyIndex)
24  {
25  if (time < KeyFrames[keyIndex + 1].Time)
26  break;
27  }
28 
29  // TODO: Check if before or after curve (and on those limits)
30 
31  // Exact value, just returns it.
32  if (time == KeyFrames[keyIndex].Time)
33  return KeyFrames[keyIndex].Value;
34 
35  // http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Interpolation_on_the_unit_interval_without_exact_derivatives
36  long timeStart = KeyFrames[keyIndex + 0].Time.Ticks;
37  long timeEnd = KeyFrames[keyIndex + 1].Time.Ticks;
38 
39  float t = ((float)time.Ticks - (float)timeStart) / ((float)timeEnd - (float)timeStart);
40 
41  var evaluator = new EvaluatorData();
42  evaluator.ValuePrev = KeyFrames[keyIndex > 0 ? keyIndex - 1 : 0];
43  evaluator.ValueStart = KeyFrames[keyIndex + 0];
44  evaluator.ValueEnd = KeyFrames[keyIndex + 1];
45  evaluator.ValueNext = KeyFrames[keyIndex + 2 < KeyFrames.Count ? keyIndex + 2 : KeyFrames.Count - 1];
46 
47  return evaluator.Evaluate(t);
48  }
49 
50  /// <summary>
51  /// Evaluates the error within specified segment.
52  /// </summary>
53  /// <param name="originalCurve">The original curve.</param>
54  /// <param name="evaluator">The evaluator.</param>
55  /// <param name="stepSize">Size of the step.</param>
56  /// <param name="keyFrame">The key frame.</param>
57  /// <param name="nextKeyFrame">The next key frame.</param>
58  /// <returns></returns>
59  private KeyValuePair<CompressedTimeSpan, float> EvaluateError(Func<CompressedTimeSpan, float> originalCurve, Evaluator evaluator, CompressedTimeSpan stepSize, KeyFrameData<float> keyFrame, KeyFrameData<float> nextKeyFrame)
60  {
61  var startTime = keyFrame.Time;
62  var endTime = nextKeyFrame.Time;
63 
64  var biggestDifference = 0.0f;
65  var biggestDifferenceTime = startTime;
66 
67  // Rounds up start time (i.e. startTime is multiple of stepSize)
68  startTime = new CompressedTimeSpan((startTime.Ticks / stepSize.Ticks + 1) * stepSize.Ticks);
69 
70  for (var time = startTime; time < endTime; time += stepSize)
71  {
72  var difference = Math.Abs(originalCurve(time) - evaluator.Evaluate(time));
73 
74  if (difference > biggestDifference)
75  {
76  biggestDifference = difference;
77  biggestDifferenceTime = time;
78  }
79  }
80  return new KeyValuePair<CompressedTimeSpan, float>(biggestDifferenceTime, biggestDifference);
81  }
82 
83  class ErrorComparer : IComparer<LinkedListNode<ErrorNode>>
84  {
85  public int Compare(LinkedListNode<ErrorNode> x, LinkedListNode<ErrorNode> y)
86  {
87  if (x.Value.Error != y.Value.Error)
88  return Math.Sign(x.Value.Error - y.Value.Error);
89 
90  return x.Value.GetHashCode() - y.Value.GetHashCode();
91  }
92  }
93 
94  public class ErrorNode
95  {
96  public LinkedListNode<KeyFrameData<float>> KeyFrame;
98  public float Error;
99 
100  public ErrorNode(LinkedListNode<KeyFrameData<float>> keyFrame, CompressedTimeSpan biggestDeltaTime, float error)
101  {
102  KeyFrame = keyFrame;
103  BiggestDeltaTime = biggestDeltaTime;
104  Error = error;
105  }
106  }
107 
108  public void Fitting(Func<CompressedTimeSpan, float> originalCurve, CompressedTimeSpan stepSize, float maxErrorThreshold)
109  {
110  // Some info: http://wscg.zcu.cz/wscg2008/Papers_2008/full/A23-full.pdf
111  // Compression of Temporal Video Data by Catmull-Rom Spline and Quadratic Bézier Curve Fitting
112  // And: http://bitsquid.blogspot.jp/2009/11/bitsquid-low-level-animation-system.html
113 
114  // Only one or zero keys: no need to do anything.
115  if (KeyFrames.Count <= 1)
116  return;
117 
118  var keyFrames = new LinkedList<KeyFrameData<float>>(this.KeyFrames);
119  var evaluator = new Evaluator(keyFrames);
120 
121  // Compute initial errors (using Least Square Equation)
122  var errors = new LinkedList<ErrorNode>();
123  //var errors = new List<float>();
124  var errorQueue = new PriorityQueue<LinkedListNode<ErrorNode>>(new ErrorComparer());
125  foreach (var keyFrame in keyFrames.EnumerateNodes())
126  {
127  if (keyFrame.Next == null)
128  break;
129  var error = EvaluateError(originalCurve, evaluator, stepSize, keyFrame.Value, keyFrame.Next.Value);
130  var errorNode = errors.AddLast(new ErrorNode(keyFrame, error.Key, error.Value));
131  errorQueue.Enqueue(errorNode);
132  }
133  //for (int keyFrame = 0; keyFrame < KeyFrames.Count - 1; ++keyFrame)
134  //{
135  // //errors.Add(EvaluateError(originalCurve, evaluator, stepSize, keyFrame));
136  // var errorNode = errors.AddLast(new ErrorNode(EvaluateError(originalCurve, evaluator, stepSize, keyFrame)));
137  // errorQueue.Enqueue(errorNode);
138  //}
139 
140  while (true)
141  {
142  // Already reached enough subdivisions
143  var highestError = errorQueue.Dequeue();
144  if (highestError.Value.Error <= maxErrorThreshold)
145  break;
146 
147  //// Find segment to optimize
148  //var biggestErrorIndex = 0;
149  //for (int keyFrame = 1; keyFrame < KeyFrames.Count - 1; ++keyFrame)
150  //{
151  // if (errors[keyFrame] > errors[biggestErrorIndex])
152  // biggestErrorIndex = keyFrame;
153  //}
154 
155  //// Already reached enough subdivisions
156  //if (errors[biggestErrorIndex] <= maxErrorThreshold)
157  // break;
158 
159  // Create new key (use middle point, but better heuristic might improve situation -- like point with biggest error)
160  //var middleTime = (start.Value.Time + end.Value.Time) / 2;
161  var middleTime = highestError.Value.BiggestDeltaTime;
162  //KeyFrames.Insert(biggestErrorIndex + 1, new KeyFrameData<float> { Time = middleTime, Value = originalCurve(middleTime) });
163  var newKeyFrame = keyFrames.AddAfter(highestError.Value.KeyFrame, new KeyFrameData<float> { Time = middleTime, Value = originalCurve(middleTime) });
164  //errors.Insert(biggestErrorIndex + 1, 0.0f);
165  var newError = errors.AddAfter(highestError, new ErrorNode(newKeyFrame, CompressedTimeSpan.Zero, 0.0f));
166 
167  var highestErrorLastUpdate = newError;
168  if (highestErrorLastUpdate.Next != null)
169  highestErrorLastUpdate = highestErrorLastUpdate.Next;
170 
171  // Invalidate evaluator (data changed)
172  evaluator.InvalidateTime();
173 
174  // Update errors
175  for (var error = highestError.Previous ?? highestError; error != null; error = error.Next)
176  {
177  if (error != highestError && error != newError)
178  errorQueue.Remove(error);
179 
180  var errorInfo = EvaluateError(originalCurve, evaluator, stepSize, error.Value.KeyFrame.Value, error.Value.KeyFrame.Next.Value);
181  error.Value.BiggestDeltaTime = errorInfo.Key;
182  error.Value.Error = errorInfo.Value;
183 
184  errorQueue.Enqueue(error);
185 
186  if (error == highestErrorLastUpdate)
187  break;
188  }
189  }
190 
191  KeyFrames = new List<KeyFrameData<float>>(keyFrames);
192  }
193 
194  public List<KeyFrameData<float>> KeyFrames { get; set; }
195 
196  /// <summary>
197  /// Gets or sets the target object name.
198  /// </summary>
199  /// <value>
200  /// The target object name.
201  /// </value>
202  public string TargetObject { get; set; }
203 
204  /// <summary>
205  /// Gets or sets the target property name.
206  /// </summary>
207  /// <value>
208  /// The target property name.
209  /// </value>
210  public string TargetProperty { get; set; }
211 
212  internal static void InitializeAnimation(ref EvaluatorData animationChannel, ref AnimationInitialValues<float> animationValue)
213  {
214  animationChannel.ValuePrev = animationValue.Value1;
215  animationChannel.ValueStart = animationValue.Value1;
216  animationChannel.ValueEnd = animationValue.Value1;
217  animationChannel.ValueNext = animationValue.Value2;
218  }
219 
220 
221  internal static void UpdateAnimation(ref EvaluatorData animationChannel, ref KeyFrameData<float> animationValue)
222  {
223  animationChannel.ValuePrev = animationChannel.ValueStart;
224  animationChannel.ValueStart = animationChannel.ValueEnd;
225  animationChannel.ValueEnd = animationChannel.ValueNext;
226  animationChannel.ValueNext = animationValue;
227  }
228 
229  public class Evaluator
230  {
231  private EvaluatorData data;
232  private CompressedTimeSpan currentTime;
233  private bool reachedEnd;
234  private IEnumerable<KeyFrameData<float>> keyFrames;
235  private IEnumerator<KeyFrameData<float>> currentKeyFrame;
236  //private KeyFrameData<float> ValueStart;
237  //private KeyFrameData<float> ValueEnd;
238 
239  public Evaluator(IEnumerable<KeyFrameData<float>> keyFrames)
240  {
241  this.keyFrames = keyFrames;
242  //this.ValueStart = keyFrames.First();
243  //this.ValueEnd = keyFrames.Last();
244  InvalidateTime();
245  }
246 
247  public float Evaluate(CompressedTimeSpan time)
248  {
249  SetTime(time);
250 
251  var startTime = data.ValueStart.Time;
252  var endTime = data.ValueEnd.Time;
253 
254  if (currentTime < startTime || currentTime > endTime)
255  {
256 
257  }
258 
259  // Sampling before start (should not really happen because we add a keyframe at TimeSpan.Zero, but let's keep it in case it changes later.
260  if (currentTime <= startTime)
261  return data.ValueStart.Value;
262 
263  // Sampling after end
264  if (currentTime >= endTime)
265  return data.ValueEnd.Value;
266 
267  // Sampling with catmull rom implicit approximation
268  float factor = (float)(currentTime - startTime).Ticks / (float)(endTime - startTime).Ticks;
269  return data.Evaluate(factor);
270  }
271 
272  public void InvalidateTime()
273  {
274  reachedEnd = false;
275 
276  currentKeyFrame = keyFrames.GetEnumerator();
277 
278  var animationInitialValues = new AnimationInitialValues<float>();
279 
280  // Skip two elements (right before third)
281  currentKeyFrame.MoveNext();
282  animationInitialValues.Value1 = currentKeyFrame.Current;
283  currentKeyFrame.MoveNext();
284  animationInitialValues.Value2 = currentKeyFrame.Current;
285 
286  currentTime = animationInitialValues.Value1.Time;
287 
288  InitializeAnimation(ref data, ref animationInitialValues);
289  }
290 
291  private void SetTime(CompressedTimeSpan timeSpan)
292  {
293  // TODO: Add jump frames to do faster seeking.
294  // If user seek back, need to start from beginning
295  if (timeSpan < currentTime)
296  {
297  InvalidateTime();
298  }
299 
300  currentTime = timeSpan;
301 
302  // Advance until requested time is reached
303  while (!(currentTime >= data.ValueStart.Time && currentTime < data.ValueEnd.Time) && !reachedEnd)
304  {
305  var moveNextFrame = currentKeyFrame.MoveNext();
306  if (!moveNextFrame)
307  {
308  reachedEnd = true;
309  UpdateAnimation(ref data, ref data.ValueNext);
310  UpdateAnimation(ref data, ref data.ValueNext);
311  break;
312  }
313  var keyFrame = moveNextFrame ? currentKeyFrame.Current : data.ValueNext;
314  UpdateAnimation(ref data, ref keyFrame);
315  }
316 
317  currentTime = timeSpan;
318  }
319  }
320 
321  public struct EvaluatorData
322  {
323  public float Evaluate(float t)
324  {
325  // http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Interpolation_on_the_unit_interval_without_exact_derivatives
326  float t2 = t * t;
327  float t3 = t2 * t;
328 
329  float factor0 = -t3 + 2.0f * t2 - t;
330  float factor1 = 3.0f * t3 - 5.0f * t2 + 2.0f;
331  float factor2 = -3.0f * t3 + 4.0f * t2 + t;
332  float factor3 = t3 - t2;
333 
334  return 0.5f * (ValuePrev.Value * factor0
335  + ValueStart.Value * factor1
336  + ValueEnd.Value * factor2
337  + ValueNext.Value * factor3);
338  }
339 
340  public KeyFrameData<float> ValuePrev;
341  public KeyFrameData<float> ValueStart;
342  public KeyFrameData<float> ValueEnd;
343  public KeyFrameData<float> ValueNext;
344  }
345  }
346 }
Evaluator(IEnumerable< KeyFrameData< float >> keyFrames)
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
Definition: DirectXTexP.h:191
List of float key frame data applying to a specific property in a node.
ErrorNode(LinkedListNode< KeyFrameData< float >> keyFrame, CompressedTimeSpan biggestDeltaTime, float error)
LinkedListNode< KeyFrameData< float > > KeyFrame
float EvaluateCubic(CompressedTimeSpan time)
void Fitting(Func< CompressedTimeSpan, float > originalCurve, CompressedTimeSpan stepSize, float maxErrorThreshold)