SkinningSample.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // SkinningSample.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 Microsoft.Xna.Framework;
  12. using Microsoft.Xna.Framework.Content;
  13. using Microsoft.Xna.Framework.Graphics;
  14. using Microsoft.Xna.Framework.Input;
  15. using SkinnedModel;
  16. using Primitives3D;
  17. #endregion
  18. namespace SkinningSample
  19. {
  20. /// <summary>
  21. /// Sample game showing how to display skinned character animation.
  22. /// </summary>
  23. public class SkinningSampleGame : Microsoft.Xna.Framework.Game
  24. {
  25. #region Fields
  26. GraphicsDeviceManager graphics;
  27. KeyboardState currentKeyboardState = new KeyboardState();
  28. GamePadState currentGamePadState = new GamePadState();
  29. KeyboardState previousKeyboardState = new KeyboardState();
  30. GamePadState previousGamePadState = new GamePadState();
  31. SkinnedSphere[] skinnedSpheres;
  32. BoundingSphere[] boundingSpheres;
  33. bool showSpheres;
  34. SpherePrimitive spherePrimitive;
  35. Model currentModel;
  36. AnimationPlayer animationPlayer;
  37. SkinningData skinningData;
  38. Matrix[] boneTransforms;
  39. Model baseballBat;
  40. float cameraArc = 0;
  41. float cameraRotation = 0;
  42. float cameraDistance = 100;
  43. #endregion
  44. #region Initialization
  45. public SkinningSampleGame()
  46. {
  47. graphics = new GraphicsDeviceManager(this);
  48. Content.RootDirectory = "Content";
  49. #if WINDOWS_PHONE
  50. // Frame rate is 30 fps by default for Windows Phone.
  51. TargetElapsedTime = TimeSpan.FromTicks(333333);
  52. graphics.IsFullScreen = true;
  53. #endif
  54. }
  55. /// <summary>
  56. /// Load your graphics content.
  57. /// </summary>
  58. protected override void LoadContent()
  59. {
  60. // Load the model.
  61. currentModel = Content.Load<Model>("dude");
  62. // Look up our custom skinning information.
  63. skinningData = currentModel.Tag as SkinningData;
  64. if (skinningData == null)
  65. throw new InvalidOperationException
  66. ("This model does not contain a SkinningData tag.");
  67. boneTransforms = new Matrix[skinningData.BindPose.Count];
  68. // Load the baseball bat model.
  69. baseballBat = Content.Load<Model>("baseballbat");
  70. // Create an animation player, and start decoding an animation clip.
  71. animationPlayer = new AnimationPlayer(skinningData);
  72. AnimationClip clip = skinningData.AnimationClips["Take 001"];
  73. animationPlayer.StartClip(clip);
  74. // Load the bounding spheres.
  75. skinnedSpheres = Content.Load<SkinnedSphere[]>("CollisionSpheres");
  76. boundingSpheres = new BoundingSphere[skinnedSpheres.Length];
  77. spherePrimitive = new SpherePrimitive(GraphicsDevice, 1, 12);
  78. }
  79. #endregion
  80. #region Update and Draw
  81. /// <summary>
  82. /// Allows the game to run logic.
  83. /// </summary>
  84. protected override void Update(GameTime gameTime)
  85. {
  86. HandleInput();
  87. UpdateCamera(gameTime);
  88. // Read gamepad inputs.
  89. float headRotation = currentGamePadState.ThumbSticks.Left.X;
  90. float armRotation = Math.Max(currentGamePadState.ThumbSticks.Left.Y, 0);
  91. // Read keyboard inputs.
  92. if (currentKeyboardState.IsKeyDown(Keys.PageUp))
  93. headRotation = -1;
  94. else if (currentKeyboardState.IsKeyDown(Keys.PageDown))
  95. headRotation = 1;
  96. if (currentKeyboardState.IsKeyDown(Keys.Space))
  97. armRotation = 0.5f;
  98. // Create rotation matrices for the head and arm bones.
  99. Matrix headTransform = Matrix.CreateRotationX(headRotation);
  100. Matrix armTransform = Matrix.CreateRotationY(-armRotation);
  101. // Tell the animation player to compute the latest bone transform matrices.
  102. animationPlayer.UpdateBoneTransforms(gameTime.ElapsedGameTime, true);
  103. // Copy the transforms into our own array, so we can safely modify the values.
  104. animationPlayer.GetBoneTransforms().CopyTo(boneTransforms, 0);
  105. // Modify the transform matrices for the head and upper-left arm bones.
  106. int headIndex = skinningData.BoneIndices["Head"];
  107. int armIndex = skinningData.BoneIndices["L_UpperArm"];
  108. boneTransforms[headIndex] = headTransform * boneTransforms[headIndex];
  109. boneTransforms[armIndex] = armTransform * boneTransforms[armIndex];
  110. // Tell the animation player to recompute the world and skin matrices.
  111. animationPlayer.UpdateWorldTransforms(Matrix.Identity, boneTransforms);
  112. animationPlayer.UpdateSkinTransforms();
  113. UpdateBoundingSpheres();
  114. base.Update(gameTime);
  115. }
  116. /// <summary>
  117. /// Updates the boundingSpheres array to match the current animation state.
  118. /// </summary>
  119. void UpdateBoundingSpheres()
  120. {
  121. // Look up the current world space bone positions.
  122. Matrix[] worldTransforms = animationPlayer.GetWorldTransforms();
  123. for (int i = 0; i < skinnedSpheres.Length; i++)
  124. {
  125. // Convert the SkinnedSphere description to a BoundingSphere.
  126. SkinnedSphere source = skinnedSpheres[i];
  127. Vector3 center = new Vector3(source.Offset, 0, 0);
  128. BoundingSphere sphere = new BoundingSphere(center, source.Radius);
  129. // Transform the BoundingSphere by its parent bone matrix,
  130. // and store the result into the boundingSpheres array.
  131. int boneIndex = skinningData.BoneIndices[source.BoneName];
  132. boundingSpheres[i] = sphere.Transform(worldTransforms[boneIndex]);
  133. }
  134. }
  135. /// <summary>
  136. /// This is called when the game should draw itself.
  137. /// </summary>
  138. protected override void Draw(GameTime gameTime)
  139. {
  140. GraphicsDevice device = graphics.GraphicsDevice;
  141. device.Clear(Color.CornflowerBlue);
  142. Matrix[] bones = animationPlayer.GetSkinTransforms();
  143. // Compute camera matrices.
  144. Matrix view = Matrix.CreateTranslation(0, -40, 0) *
  145. Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
  146. Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
  147. Matrix.CreateLookAt(new Vector3(0, 0, -cameraDistance),
  148. new Vector3(0, 0, 0), Vector3.Up);
  149. Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
  150. device.Viewport.AspectRatio,
  151. 1,
  152. 10000);
  153. // Render the skinned mesh.
  154. foreach (ModelMesh mesh in currentModel.Meshes)
  155. {
  156. foreach (SkinnedEffect effect in mesh.Effects)
  157. {
  158. effect.SetBoneTransforms(bones);
  159. effect.View = view;
  160. effect.Projection = projection;
  161. effect.EnableDefaultLighting();
  162. effect.SpecularColor = new Vector3(0.25f);
  163. effect.SpecularPower = 16;
  164. }
  165. mesh.Draw();
  166. }
  167. DrawBaseballBat(view, projection);
  168. if (showSpheres)
  169. {
  170. DrawBoundingSpheres(view, projection);
  171. }
  172. base.Draw(gameTime);
  173. }
  174. /// <summary>
  175. /// Draws the animated bounding spheres.
  176. /// </summary>
  177. void DrawBoundingSpheres(Matrix view, Matrix projection)
  178. {
  179. GraphicsDevice.RasterizerState = Wireframe;
  180. foreach (BoundingSphere sphere in boundingSpheres)
  181. {
  182. Matrix world = Matrix.CreateScale(sphere.Radius) *
  183. Matrix.CreateTranslation(sphere.Center);
  184. spherePrimitive.Draw(world, view, projection, Color.White);
  185. }
  186. GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
  187. }
  188. static RasterizerState Wireframe = new RasterizerState
  189. {
  190. FillMode = FillMode.WireFrame
  191. };
  192. /// <summary>
  193. /// Draws the baseball bat.
  194. /// </summary>
  195. void DrawBaseballBat(Matrix view, Matrix projection)
  196. {
  197. int handIndex = skinningData.BoneIndices["L_Index1"];
  198. Matrix[] worldTransforms = animationPlayer.GetWorldTransforms();
  199. // Nudge the bat over so it appears between the left thumb and index finger.
  200. Matrix batWorldTransform = Matrix.CreateTranslation(-1.3f, 2.1f, 0.1f) *
  201. worldTransforms[handIndex];
  202. foreach (ModelMesh mesh in baseballBat.Meshes)
  203. {
  204. foreach (BasicEffect effect in mesh.Effects)
  205. {
  206. effect.World = batWorldTransform;
  207. effect.View = view;
  208. effect.Projection = projection;
  209. effect.EnableDefaultLighting();
  210. }
  211. mesh.Draw();
  212. }
  213. }
  214. #endregion
  215. #region Handle Input
  216. /// <summary>
  217. /// Handles input for quitting the game.
  218. /// </summary>
  219. private void HandleInput()
  220. {
  221. previousKeyboardState = currentKeyboardState;
  222. previousGamePadState = currentGamePadState;
  223. currentKeyboardState = Keyboard.GetState();
  224. currentGamePadState = GamePad.GetState(PlayerIndex.One);
  225. // Check for exit.
  226. if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
  227. currentGamePadState.Buttons.Back == ButtonState.Pressed)
  228. {
  229. Exit();
  230. }
  231. // Toggle the collision sphere display.
  232. if ((currentKeyboardState.IsKeyDown(Keys.Enter) &&
  233. previousKeyboardState.IsKeyUp(Keys.Enter)) ||
  234. (currentGamePadState.IsButtonDown(Buttons.A) &&
  235. previousGamePadState.IsButtonUp(Buttons.A)))
  236. {
  237. showSpheres = !showSpheres;
  238. }
  239. }
  240. /// <summary>
  241. /// Handles camera input.
  242. /// </summary>
  243. private void UpdateCamera(GameTime gameTime)
  244. {
  245. float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
  246. // Check for input to rotate the camera up and down around the model.
  247. if (currentKeyboardState.IsKeyDown(Keys.Up) ||
  248. currentKeyboardState.IsKeyDown(Keys.W))
  249. {
  250. cameraArc += time * 0.1f;
  251. }
  252. if (currentKeyboardState.IsKeyDown(Keys.Down) ||
  253. currentKeyboardState.IsKeyDown(Keys.S))
  254. {
  255. cameraArc -= time * 0.1f;
  256. }
  257. cameraArc += currentGamePadState.ThumbSticks.Right.Y * time * 0.25f;
  258. // Limit the arc movement.
  259. if (cameraArc > 90.0f)
  260. cameraArc = 90.0f;
  261. else if (cameraArc < -90.0f)
  262. cameraArc = -90.0f;
  263. // Check for input to rotate the camera around the model.
  264. if (currentKeyboardState.IsKeyDown(Keys.Right) ||
  265. currentKeyboardState.IsKeyDown(Keys.D))
  266. {
  267. cameraRotation += time * 0.1f;
  268. }
  269. if (currentKeyboardState.IsKeyDown(Keys.Left) ||
  270. currentKeyboardState.IsKeyDown(Keys.A))
  271. {
  272. cameraRotation -= time * 0.1f;
  273. }
  274. cameraRotation += currentGamePadState.ThumbSticks.Right.X * time * 0.25f;
  275. // Check for input to zoom camera in and out.
  276. if (currentKeyboardState.IsKeyDown(Keys.Z))
  277. cameraDistance += time * 0.25f;
  278. if (currentKeyboardState.IsKeyDown(Keys.X))
  279. cameraDistance -= time * 0.25f;
  280. cameraDistance += currentGamePadState.Triggers.Left * time * 0.5f;
  281. cameraDistance -= currentGamePadState.Triggers.Right * time * 0.5f;
  282. // Limit the camera distance.
  283. if (cameraDistance > 500.0f)
  284. cameraDistance = 500.0f;
  285. else if (cameraDistance < 10.0f)
  286. cameraDistance = 10.0f;
  287. if (currentGamePadState.Buttons.RightStick == ButtonState.Pressed ||
  288. currentKeyboardState.IsKeyDown(Keys.R))
  289. {
  290. cameraArc = 0;
  291. cameraRotation = 0;
  292. cameraDistance = 100;
  293. }
  294. }
  295. #endregion
  296. }
  297. #region Entry Point
  298. /// <summary>
  299. /// The main entry point for the application.
  300. /// </summary>
  301. static class Program
  302. {
  303. static void Main()
  304. {
  305. using (SkinningSampleGame game = new SkinningSampleGame())
  306. {
  307. game.Run();
  308. }
  309. }
  310. }
  311. #endregion
  312. }