PageRenderTime 85ms CodeModel.GetById 30ms app.highlight 16ms RepoModel.GetById 34ms app.codeStats 1ms

/Rendering/Models/Animation.cs

#
C# | 498 lines | 265 code | 65 blank | 168 comment | 21 complexity | d109824764e68510e39519425d38d1ed MD5 | raw file
  1using Delta.ContentSystem.Rendering;
  2using Delta.Engine.Game;
  3using Delta.Utilities;
  4using Delta.Utilities.Datatypes;
  5using Delta.Utilities.Helpers;
  6
  7namespace Delta.Rendering.Models
  8{
  9	/// <summary>
 10	/// Simple animation class for mesh skinning.
 11	/// </summary>
 12	public class Animation
 13	{
 14		#region AnimationInfo Struct
 15		/// <summary>
 16		/// Animation info
 17		/// </summary>
 18		private struct AnimationInfo
 19		{
 20			#region Internal
 21
 22			#region Name (Internal)
 23			/// <summary>
 24			/// Name of the current animation.
 25			/// </summary>
 26			internal string Name;
 27			#endregion
 28
 29			#region StartIndex (Internal)
 30			/// <summary>
 31			/// The start frame index of the active animation.
 32			/// </summary>
 33			internal int StartIndex;
 34			#endregion
 35
 36			#region EndIndex (Internal)
 37			/// <summary>
 38			/// The end frame index of the active animation.
 39			/// </summary>
 40			internal int EndIndex;
 41			#endregion
 42
 43			#region FrameRate (Internal)
 44			/// <summary>
 45			/// The speed that the animation will run in. (Frames per second)
 46			/// </summary>
 47			internal float FrameRate;
 48			#endregion
 49
 50			#region DefaultFrameRate (Internal)
 51			/// <summary>
 52			/// Default framerate of current animation, 
 53			/// which was set at SetCurrentAnimation.
 54			/// </summary>
 55			internal float DefaultFrameRate;
 56			#endregion
 57
 58			#region Loop (Internal)
 59			/// <summary>
 60			/// True if the animation should only run once then stop.
 61			/// </summary>
 62			internal bool Loop;
 63			#endregion
 64
 65			#region FramesCount (Internal)
 66			/// <summary>
 67			/// Number of animation frames for currently played animation
 68			/// </summary>
 69			internal int FramesCount;
 70			#endregion
 71
 72			#region FrameIndex (Internal)
 73			/// <summary>
 74			/// The current frame index of the active animation.
 75			/// </summary>
 76			internal int FrameIndex;
 77			#endregion
 78
 79			#region FrameTimer (Internal)
 80			/// <summary>
 81			/// The time in seconds since the last frame index increase.
 82			/// </summary>
 83			internal float FrameTimer;
 84			#endregion
 85
 86			#region BoneMatrices (Internal)
 87			/// <summary>
 88			/// Current bone matrices of the animation.
 89			/// </summary>
 90			internal Matrix[] BoneMatrices;
 91			#endregion
 92
 93			#endregion
 94		}
 95		#endregion
 96
 97		#region Constants
 98		/// <summary>
 99		/// The frame rate as a constant for now.
100		/// </summary>
101		private const int FrameRate = 30;
102		#endregion
103
104		#region Duration (Public)
105		/// <summary>
106		/// Length of the Animation in seconds.
107		/// </summary>
108		public float Duration
109		{
110			get;
111			set;
112		}
113		#endregion
114
115		#region FrameCount (Public)
116		/// <summary>
117		/// Number of frames.
118		/// </summary>
119		public int FrameCount
120		{
121			get
122			{
123				return Frames.Length;
124			}
125		}
126		#endregion
127
128		#region Frames (Public)
129		/// <summary>
130		/// Array of animation frames (as array of bone matrices)
131		/// e.g.
132		/// Frames[0] -> Frame 1
133		///   Frames[0][0] -> Bone Matrix 1
134		///   Frames[0][1] -> Bone Matrix 2
135		/// Frames[1] -> Frame 2
136		///   Frames[1][0] -> Bone Matrix 1
137		///   Frames[1][1] -> Bone Matrix 2
138		/// </summary>
139		public Matrix[][] Frames;
140		#endregion
141
142		#region NextFrame (Public)
143		/// <summary>
144		/// Next frame
145		/// </summary>
146		public int NextFrame
147		{
148			get
149			{
150				return nextFrame;
151			}
152		}
153		#endregion
154
155		#region Private
156
157		#region parent (Private)
158		/// <summary>
159		/// The parent mesh of this animation.
160		/// </summary>
161		private readonly Mesh parent;
162		#endregion
163
164		#region animation (Private)
165		/// <summary>
166		/// Animation data / info.
167		/// </summary>
168		private AnimationInfo animation;
169		#endregion
170
171		#region blendAnimation (Private)
172		/// <summary>
173		/// Blend animation data / info.
174		/// </summary>
175		private AnimationInfo blendAnimation;
176		#endregion
177
178		#region onBlending (Private)
179		/// <summary>
180		/// Currently on blending an animation.
181		/// </summary>
182		private bool onBlending;
183		#endregion
184
185		#region blendingTime (Private)
186		/// <summary>
187		/// Animation blending time.
188		/// </summary>
189		private float blendingTime;
190		#endregion
191
192		#region currentBlendingTime (Private)
193		/// <summary>
194		/// Current time of the animation blending.
195		/// </summary>
196		private float currentBlendingTime;
197		#endregion
198
199		#region nextFrame (Private)
200		/// <summary>
201		/// The next frame we want to interpolate to.
202		/// </summary>
203		private int nextFrame;
204		#endregion
205
206		#endregion
207
208		#region Constructors
209		/// <summary>
210		/// Create a new animation.
211		/// </summary>
212		/// <param name="setParent">The parent mesh of this animation.</param>
213		/// <param name="setAnimationData">The data of this animation.</param>
214		public Animation(Mesh setParent, MeshAnimationData setAnimationData)
215		{
216			parent = setParent;
217			Frames = setAnimationData.Frames;
218
219			animation = new AnimationInfo();
220			blendAnimation = new AnimationInfo();
221
222			// Set the animation values.
223			animation.StartIndex = 0;
224			animation.EndIndex = Frames.GetLength(0);
225			animation.FrameRate = 30;
226			animation.DefaultFrameRate = animation.FrameRate;
227			animation.BoneMatrices = parent.Geometry.FinalBoneMatrices;
228			animation.FrameIndex = 0;
229			animation.FramesCount = 0;
230			animation.FrameTimer = 0.0f;
231			animation.Loop = false;
232			animation.Name = "";
233
234			// Set the blend animation values.
235			blendAnimation = animation;
236
237			// Set default. No animation blending.
238			onBlending = false;
239		}
240		#endregion
241
242		#region SetAnimationSpeed (Public)
243		/// <summary>
244		/// Set animation speed.
245		/// </summary>
246		/// <param name="factor">Scale factor to apply.</param>
247		public void SetAnimationSpeed(float factor)
248		{
249			animation.FrameRate = (factor * animation.DefaultFrameRate);
250		}
251		#endregion
252
253		#region SetCurrentAnimation (Public)
254		/// <summary>
255		/// Change the current animation.
256		/// </summary>
257		/// <param name="animaitonName">The animation name to switch to.</param>
258		/// <param name="indexStart">Animation index start.</param>
259		/// <param name="indexEnd">Animation index end.</param>
260		/// <param name="frameRate">Frame rate during animation playback.</param>
261		/// <param name="loop">True to loop animation, false otherwise.</param>
262		public void SetCurrentAnimation(string animaitonName, int indexStart,
263			int indexEnd, float frameRate, bool loop)
264		{
265			SetAnimation(ref animation, animaitonName, indexStart, indexEnd,
266				frameRate, loop);
267			onBlending = false;
268		}
269		#endregion
270
271		#region BlendAnimation (Public)
272		/// <summary>
273		/// Blend animation with current animation playing.
274		/// </summary>
275		/// <param name="animationName">The animation name to blend with.</param>
276		/// <param name="indexStart">Animation index start.</param>
277		/// <param name="indexEnd">Animation index end.</param>
278		/// <param name="frameRate">Frame rate during animation playback.</param>
279		/// <param name="loop">True to loop animation, false otherwise.</param>
280		/// <param name="blendingTime">Blending duration time.</param>
281		public void BlendAnimation(string animationName, int indexStart,
282			int indexEnd, float frameRate, bool loop, float blendingTime)
283		{
284			onBlending = true;
285
286			if (animation.FramesCount == 0)
287			{
288				//Log.Info("There are no animation to blend. " + 
289				//  "Normal play of the given animation.");
290				// No blending.
291				onBlending = false;
292			}
293
294			blendAnimation = animation;
295			SetAnimation(ref animation, animationName, indexStart, indexEnd,
296				frameRate, loop);
297
298			this.blendingTime = blendingTime;
299			currentBlendingTime = 0.0f;
300		}
301		#endregion
302
303		#region Update (Public)
304		/// <summary>
305		/// Update the animation.
306		/// </summary>
307		public bool Update()
308		{
309			//long totalTimeMs = Time.Milliseconds;
310			//int frameRate = 30;
311
312			// Check if there are any keyframes, if not then return.
313			int numOfAnimations = Frames.GetLength(0);
314			if (numOfAnimations == 0)
315			{
316				Log.Info("There are no keyframes for animation.");
317				return false;
318			}
319
320			// Update the current animation.
321			bool bResult = UpdateAnimation(ref animation);
322
323			if (onBlending)
324			{
325				// Update the blending animation.
326				UpdateAnimation(ref blendAnimation);
327
328				// Increase the current blending time.
329				currentBlendingTime += GameTime.Delta;
330
331				// The blending is over.
332				if (currentBlendingTime > blendingTime)
333				{
334					onBlending = false;
335				}
336
337				float blendFactor = currentBlendingTime / blendingTime;
338
339				Matrix[] finalBoneMatrices =
340					new Matrix[blendAnimation.BoneMatrices.Length];
341
342				// Calculate the final bone matrices.
343				for (int i = 0; i < finalBoneMatrices.Length; i++)
344				{
345					finalBoneMatrices[i] =
346						Matrix.Multiply(blendAnimation.BoneMatrices[i], (1.0f - blendFactor)) +
347						Matrix.Multiply(animation.BoneMatrices[i], blendFactor);
348				}
349
350				// Set the final bone matrices.
351				parent.Geometry.FinalBoneMatrices = finalBoneMatrices;
352			}
353			else
354			{
355				parent.Geometry.FinalBoneMatrices = animation.BoneMatrices;
356			}
357
358			return bResult;
359		}
360		#endregion
361
362		#region Methods (Private)
363
364		#region UpdateAnimation
365		/// <summary>
366		/// Update animation simulation.
367		/// </summary>
368		/// <param name="currentAnimation">Animation info to update with.</param>
369		/// <returns>True if animation correctly updated, false otherwise.</returns>
370		private bool UpdateAnimation(ref AnimationInfo currentAnimation)
371		{
372			currentAnimation.FrameTimer += GameTime.Delta *
373			                               (currentAnimation.FrameRate /
374			                                currentAnimation.DefaultFrameRate);
375
376			float secondsPerFrame = 1 / currentAnimation.DefaultFrameRate;
377			Duration += secondsPerFrame;
378
379			if (currentAnimation.Loop == false && currentAnimation.FrameIndex >=
380			                                      currentAnimation.FramesCount - 1 &&
381			    currentAnimation.FrameIndex > 0)
382			{
383				currentAnimation.FrameIndex = currentAnimation.FramesCount - 1;
384				currentAnimation.BoneMatrices = Frames[currentAnimation.FrameIndex +
385				                                       currentAnimation.StartIndex];
386				return true;
387			}
388
389			while (currentAnimation.FrameTimer >= secondsPerFrame)
390			{
391				currentAnimation.FrameTimer -= secondsPerFrame;
392				currentAnimation.FrameIndex++;
393
394				// Returning true if the animation is complete and is not looping.
395				// If the animation is looping then we just null currentFrameIndex.
396				if (currentAnimation.Loop == false &&
397				    currentAnimation.FrameIndex >= currentAnimation.FramesCount)
398				{
399					return true;
400				}
401				else if (currentAnimation.Loop)
402				{
403					currentAnimation.FrameIndex %= currentAnimation.FramesCount;
404				}
405
406				// Increase next frame so it becomes the frame we want to interpolate to
407				nextFrame = currentAnimation.FrameIndex + 1;
408				if (nextFrame >= currentAnimation.FramesCount)
409				{
410					nextFrame %= currentAnimation.FramesCount;
411				}
412
413				if (Frames[currentAnimation.FrameIndex +
414				           currentAnimation.StartIndex].Length == 0)
415				{
416					Log.Info("There are no bones that can be animated.");
417					return false;
418				}
419			} // while
420
421			// Calculate lerp
422			// NOTE: Disabled for the demo!!!!!!!!!!!!!!!!!!!!!
423			// The Animation flash sometimes and for the moment we don't need 
424			// any frame interpolations, because we have 2 different animations.
425			// Like walk and run.
426			//if (onBlending == false)
427			//if (false)
428			//{
429			//  float lerpAmount = currentAnimation.FrameTimer / secondsPerFrame;
430			//  int frameIndex = currentAnimation.FrameIndex + currentAnimation.StartIndex;
431
432			//  Matrix[] boneMatricesCopy = new Matrix[currentAnimation.BoneMatrices.Length];
433			//  //Frames[frameIndex].CopyTo(boneMatricesCopy, 0);
434
435			//  for (int i = 0; i < boneMatricesCopy.Length; i++)
436			//  {
437			//    //boneMatricesCopy[i] = Frames[frameIndex][i];
438			//    boneMatricesCopy[i] =
439			//    Matrix.Multiply(Frames[currentAnimation.FrameIndex +
440			//      currentAnimation.StartIndex][i], (1.0f - lerpAmount)) +
441			//    Matrix.Multiply(Frames[nextFrame +
442			//      currentAnimation.StartIndex][i], lerpAmount);
443			//  } // for
444
445			//  currentAnimation.BoneMatrices = boneMatricesCopy;
446			//} // if
447			//else
448			{
449				currentAnimation.BoneMatrices =
450					Frames[currentAnimation.FrameIndex + currentAnimation.StartIndex];
451			}
452
453			return false;
454		}
455		#endregion
456
457		#region SetAnimation
458		/// <summary>
459		/// Set animation info data.
460		/// </summary>
461		/// <param name="data">AnimationInfo data to apply</param>
462		/// <param name="animationName">The animation name to blend with.</param>
463		/// <param name="indexStart">Animation index start.</param>
464		/// <param name="indexEnd">Animation index end.</param>
465		/// <param name="frameRate">Frame rate during animation playback.</param>
466		/// <param name="loop">True to loop animation, false otherwise.</param>
467		private void SetAnimation(ref AnimationInfo data, string animationName,
468			int indexStart, int indexEnd, float frameRate, bool loop)
469		{
470			if (indexStart > Frames.GetLength(0))
471			{
472				Log.Info("Trying to start animation at value higher "
473				         + "then number of animation frames.");
474				return;
475			}
476
477			// Make sure that indexEnd is greater than indexStart and less then
478			// the max number of frames.
479			if (indexEnd <= indexStart || indexEnd > Frames.GetLength(0))
480			{
481				indexEnd = Frames.GetLength(0);
482			}
483
484			data.Name = animationName;
485			data.FrameIndex = 0;
486			data.StartIndex = indexStart;
487			data.EndIndex = indexEnd;
488			// Add a small random value to make animations asynchronous
489			data.FrameRate = frameRate + RandomHelper.RandomFloat(-2.0f, 2.0f);
490			data.DefaultFrameRate = frameRate;
491			data.Loop = loop;
492			data.FramesCount = (indexEnd - indexStart);
493		}
494		#endregion
495
496		#endregion
497	}
498}