PageRenderTime 41ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/Rendering/Models/Animation.cs

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