Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
AnimationProcessor.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 System.Linq;
6 using SiliconStudio.Core;
7 using SiliconStudio.Core.Collections;
8 
9 using SiliconStudio.Core.Diagnostics;
10 using SiliconStudio.Paradox.DataModel;
11 using SiliconStudio.Core.Extensions;
12 using SiliconStudio.Core.Mathematics;
13 using SiliconStudio.Paradox.Effects;
14 
15 using SiliconStudio.Paradox.EntityModel;
16 using SiliconStudio.Paradox.Games;
17 
18 namespace SiliconStudio.Paradox.Engine
19 {
20  public class AnimationProcessor : EntityProcessor<AnimationProcessor.AssociatedData>
21  {
22  private FastList<AnimationOperation> animationOperations = new FastList<AnimationOperation>(2);
23 
24 
26  : base(new PropertyKey[] { ModelComponent.Key, AnimationComponent.Key })
27  {
28  }
29 
30  protected override AssociatedData GenerateAssociatedData(Entity entity)
31  {
32  return new AssociatedData { ModelComponent = entity.Get(ModelComponent.Key), AnimationComponent = entity.Get(AnimationComponent.Key) };
33  }
34 
35  protected override void OnEntityAdding(Entity entity, AssociatedData associatedData)
36  {
37  base.OnEntityAdding(entity, associatedData);
38 
39  associatedData.MeshAnimationUpdater = new MeshAnimationUpdater();
40  }
41 
42  protected override void OnEntityRemoved(Entity entity, AnimationProcessor.AssociatedData data)
43  {
44  base.OnEntityRemoved(entity, data);
45 
46  // Return AnimationClipEvaluators to pool
47  foreach (var playingAnimation in data.AnimationComponent.PlayingAnimations)
48  {
49  var evaluator = playingAnimation.Evaluator;
50  if (evaluator != null)
51  {
52  data.AnimationComponent.Blender.ReleaseEvaluator(evaluator);
53  playingAnimation.Evaluator = null;
54  }
55  }
56 
57  // Return AnimationClipResult to pool
58  if (data.AnimationClipResult != null)
59  data.AnimationComponent.Blender.FreeIntermediateResult(data.AnimationClipResult);
60  }
61 
62  public override void Draw(GameTime time)
63  {
64  foreach (var entity in enabledEntities)
65  {
66  var associatedData = entity.Value;
67  var meshAnimation = associatedData.MeshAnimationUpdater;
68  var animationComponent = associatedData.AnimationComponent;
69 
70  // Advance time for all playing animations with AutoPlay set to on
71  foreach (var playingAnimation in animationComponent.PlayingAnimations)
72  {
73  if (playingAnimation.IsPlaying)
74  {
75  switch (playingAnimation.RepeatMode)
76  {
77  case AnimationRepeatMode.PlayOnce:
78  playingAnimation.CurrentTime = TimeSpan.FromTicks(playingAnimation.CurrentTime.Ticks + (long)(time.Elapsed.Ticks * (double)playingAnimation.TimeFactor));
79  if (playingAnimation.CurrentTime > playingAnimation.Clip.Duration)
80  playingAnimation.CurrentTime = playingAnimation.Clip.Duration;
81  break;
82  case AnimationRepeatMode.LoopInfinite:
83  playingAnimation.CurrentTime = TimeSpan.FromTicks((playingAnimation.CurrentTime.Ticks + (long)(time.Elapsed.Ticks * (double)playingAnimation.TimeFactor)) % playingAnimation.Clip.Duration.Ticks);
84  break;
85  default:
86  throw new ArgumentOutOfRangeException();
87  }
88  }
89  }
90 
91  // Regenerate animation operations
92  animationOperations.Clear();
93 
94  float totalWeight = 0.0f;
95 
96  for (int index = 0; index < animationComponent.PlayingAnimations.Count; index++)
97  {
98  var playingAnimation = animationComponent.PlayingAnimations[index];
99  var animationWeight = playingAnimation.Weight;
100 
101  // Skip animation with 0.0f weight
102  if (animationWeight == 0.0f)
103  continue;
104 
105  // Default behavior for linea blending (it will properly accumulate multiple blending with their cumulative weight)
106  totalWeight += animationWeight;
107  float currentBlend = animationWeight / totalWeight;
108 
109  if (playingAnimation.BlendOperation == AnimationBlendOperation.Add
110  || playingAnimation.BlendOperation == AnimationBlendOperation.Subtract)
111  {
112  // Additive or substractive blending will use the weight as is (and reset total weight with it)
113  currentBlend = animationWeight;
114  totalWeight = animationWeight;
115  }
116 
117  // Create evaluator
118  var evaluator = playingAnimation.Evaluator;
119  if (evaluator == null)
120  {
121  evaluator = animationComponent.Blender.CreateEvaluator(playingAnimation.Clip);
122  playingAnimation.Evaluator = evaluator;
123  }
124 
125  animationOperations.Add(CreatePushOperation(playingAnimation));
126 
127  if (animationOperations.Count >= 2)
128  animationOperations.Add(AnimationOperation.NewBlend(playingAnimation.BlendOperation, currentBlend));
129  }
130 
131  if (animationOperations.Count > 0)
132  {
133  // Animation blending
134  animationComponent.Blender.Compute(animationOperations, ref associatedData.AnimationClipResult);
135 
136  // Update animation data
137  meshAnimation.Update(associatedData.ModelComponent.ModelViewHierarchy, associatedData.AnimationClipResult);
138  }
139  else
140  {
141  // If nothing is playing, reset to bind pose
142  associatedData.ModelComponent.ModelViewHierarchy.ResetInitialValues();
143  }
144 
145  // Update weight animation
146  for (int index = 0; index < animationComponent.PlayingAnimations.Count; index++)
147  {
148  var playingAnimation = animationComponent.PlayingAnimations[index];
149  if (playingAnimation.RemainingTime > TimeSpan.Zero)
150  {
151  playingAnimation.Weight += (playingAnimation.WeightTarget - playingAnimation.Weight)*
152  ((float)time.Elapsed.Ticks/
153  (float)playingAnimation.RemainingTime.Ticks);
154  playingAnimation.RemainingTime -= time.Elapsed;
155  if (playingAnimation.RemainingTime <= TimeSpan.Zero)
156  {
157  playingAnimation.Weight = playingAnimation.WeightTarget;
158  }
159  }
160  else if (playingAnimation.Weight / totalWeight <= 0.01f)
161  {
162  animationComponent.PlayingAnimations.RemoveAt(index--);
163 
164  var evaluator = playingAnimation.Evaluator;
165  if (evaluator != null)
166  {
167  animationComponent.Blender.ReleaseEvaluator(evaluator);
168  playingAnimation.Evaluator = null;
169  }
170  }
171  }
172  }
173  }
174 
175  private AnimationOperation CreatePushOperation(PlayingAnimation playingAnimation)
176  {
177  return AnimationOperation.NewPush(playingAnimation.Evaluator, playingAnimation.CurrentTime);
178  }
179 
180  public class AssociatedData
181  {
186  }
187  }
188 }
Game entity. It usually aggregates multiple EntityComponent
Definition: Entity.cs:28
AnimationBlendOperation
Describes the type of animation blend operation.
A single animation operation (push or blend).
Applies animation from a AnimationClip to a ModelViewHierarchyUpdater.
override void OnEntityRemoved(Entity entity, AnimationProcessor.AssociatedData data)
Entity processor, triggered on various EntitySystem events such as Entity and Component additions and...
Add animation capabilities to an Entity. It will usually apply to ModelComponent.ModelViewHierarchy ...
Current timing used for variable-step (real time) or fixed-step (game time) games.
Definition: GameTime.cs:31
override void OnEntityAdding(Entity entity, AssociatedData associatedData)
Add a Model to an Entity, that will be used during rendering.
static PropertyKey< AnimationComponent > Key
override AssociatedData GenerateAssociatedData(Entity entity)
A class that represents a tag propety.
Definition: PropertyKey.cs:17