AnimationPlayer.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // AnimationPlayer.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using System.Collections.Generic;
  12. using Microsoft.Xna.Framework;
  13. #endregion
  14. namespace SkinnedModel
  15. {
  16. /// <summary>
  17. /// The animation player is in charge of decoding bone position
  18. /// matrices from an animation clip.
  19. /// </summary>
  20. public class AnimationPlayer
  21. {
  22. #region Fields
  23. // Information about the currently playing animation clip.
  24. AnimationClip currentClipValue;
  25. TimeSpan currentTimeValue;
  26. int currentKeyframe;
  27. // Current animation transform matrices.
  28. Matrix[] boneTransforms;
  29. Matrix[] worldTransforms;
  30. Matrix[] skinTransforms;
  31. // Backlink to the bind pose and skeleton hierarchy data.
  32. SkinningData skinningDataValue;
  33. #endregion
  34. /// <summary>
  35. /// Constructs a new animation player.
  36. /// </summary>
  37. public AnimationPlayer(SkinningData skinningData)
  38. {
  39. if (skinningData == null)
  40. throw new ArgumentNullException("skinningData");
  41. skinningDataValue = skinningData;
  42. boneTransforms = new Matrix[skinningData.BindPose.Count];
  43. worldTransforms = new Matrix[skinningData.BindPose.Count];
  44. skinTransforms = new Matrix[skinningData.BindPose.Count];
  45. }
  46. /// <summary>
  47. /// Starts decoding the specified animation clip.
  48. /// </summary>
  49. public void StartClip(AnimationClip clip)
  50. {
  51. if (clip == null)
  52. throw new ArgumentNullException("clip");
  53. currentClipValue = clip;
  54. currentTimeValue = TimeSpan.Zero;
  55. currentKeyframe = 0;
  56. // Initialize bone transforms to the bind pose.
  57. skinningDataValue.BindPose.CopyTo(boneTransforms, 0);
  58. }
  59. /// <summary>
  60. /// Advances the current animation position.
  61. /// </summary>
  62. public void Update(TimeSpan time, bool relativeToCurrentTime,
  63. Matrix rootTransform)
  64. {
  65. UpdateBoneTransforms(time, relativeToCurrentTime);
  66. UpdateWorldTransforms(rootTransform);
  67. UpdateSkinTransforms();
  68. }
  69. /// <summary>
  70. /// Helper used by the Update method to refresh the BoneTransforms data.
  71. /// </summary>
  72. public void UpdateBoneTransforms(TimeSpan time, bool relativeToCurrentTime)
  73. {
  74. if (currentClipValue == null)
  75. throw new InvalidOperationException(
  76. "AnimationPlayer.Update was called before StartClip");
  77. // Update the animation position.
  78. if (relativeToCurrentTime)
  79. {
  80. time += currentTimeValue;
  81. // If we reached the end, loop back to the start.
  82. while (time >= currentClipValue.Duration)
  83. time -= currentClipValue.Duration;
  84. }
  85. if ((time < TimeSpan.Zero) || (time >= currentClipValue.Duration))
  86. throw new ArgumentOutOfRangeException("time");
  87. // If the position moved backwards, reset the keyframe index.
  88. if (time < currentTimeValue)
  89. {
  90. currentKeyframe = 0;
  91. skinningDataValue.BindPose.CopyTo(boneTransforms, 0);
  92. }
  93. currentTimeValue = time;
  94. // Read keyframe matrices.
  95. IList<Keyframe> keyframes = currentClipValue.Keyframes;
  96. while (currentKeyframe < keyframes.Count)
  97. {
  98. Keyframe keyframe = keyframes[currentKeyframe];
  99. // Stop when we've read up to the current time position.
  100. if (keyframe.Time > currentTimeValue)
  101. break;
  102. // Use this keyframe.
  103. boneTransforms[keyframe.Bone] = keyframe.Transform;
  104. currentKeyframe++;
  105. }
  106. }
  107. /// <summary>
  108. /// Helper used by the Update method to refresh the WorldTransforms data.
  109. /// </summary>
  110. public void UpdateWorldTransforms(Matrix rootTransform)
  111. {
  112. // Root bone.
  113. worldTransforms[0] = boneTransforms[0] * rootTransform;
  114. // Child bones.
  115. for (int bone = 1; bone < worldTransforms.Length; bone++)
  116. {
  117. int parentBone = skinningDataValue.SkeletonHierarchy[bone];
  118. worldTransforms[bone] = boneTransforms[bone] *
  119. worldTransforms[parentBone];
  120. }
  121. }
  122. /// <summary>
  123. /// Helper used by the Update method to refresh the SkinTransforms data.
  124. /// </summary>
  125. public void UpdateSkinTransforms()
  126. {
  127. for (int bone = 0; bone < skinTransforms.Length; bone++)
  128. {
  129. skinTransforms[bone] = skinningDataValue.InverseBindPose[bone] *
  130. worldTransforms[bone];
  131. }
  132. }
  133. /// <summary>
  134. /// Gets the current bone transform matrices, relative to their parent bones.
  135. /// </summary>
  136. public Matrix[] GetBoneTransforms()
  137. {
  138. return boneTransforms;
  139. }
  140. /// <summary>
  141. /// Gets the current bone transform matrices, in absolute format.
  142. /// </summary>
  143. public Matrix[] GetWorldTransforms()
  144. {
  145. return worldTransforms;
  146. }
  147. /// <summary>
  148. /// Gets the current bone transform matrices,
  149. /// relative to the skinning bind pose.
  150. /// </summary>
  151. public Matrix[] GetSkinTransforms()
  152. {
  153. return skinTransforms;
  154. }
  155. /// <summary>
  156. /// Gets the clip currently being decoded.
  157. /// </summary>
  158. public AnimationClip CurrentClip
  159. {
  160. get { return currentClipValue; }
  161. }
  162. /// <summary>
  163. /// Gets the current play position.
  164. /// </summary>
  165. public TimeSpan CurrentTime
  166. {
  167. get { return currentTimeValue; }
  168. }
  169. }
  170. }