ObjectPlacementOnAvatar.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // ObjectPlacementOnAvatar.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. using Microsoft.Xna.Framework.GamerServices;
  14. using Microsoft.Xna.Framework.Graphics;
  15. using Microsoft.Xna.Framework.Input;
  16. #endregion
  17. namespace ObjectPlacementOnAvatar
  18. {
  19. /// <summary>
  20. /// This sample demonstrates how to place objects onto an avatar while it animates.
  21. /// </summary>
  22. public class ObjectPlacementOnAvatarGame : Microsoft.Xna.Framework.Game
  23. {
  24. #region Fields
  25. GraphicsDeviceManager graphics;
  26. // Description, renderer, and animation for
  27. // loading and drawing an animated avatar
  28. AvatarDescription avatarDescription;
  29. AvatarRenderer avatarRenderer;
  30. AvatarAnimation currentAvatarAnimation;
  31. AvatarAnimation[] avatarAnimations = new AvatarAnimation[4];
  32. // Model for the baseball bat that is going to
  33. // be placed on the avatar
  34. Model baseballBat;
  35. // Matrices used to draw the avatar
  36. Matrix world;
  37. Matrix view;
  38. Matrix projection;
  39. // List of the avatar bones in world space
  40. List<Matrix> bonesWorldSpace;
  41. // the current input states. These are updated in the HandleInput function,
  42. // and used primarily in the UpdateCamera function.
  43. GamePadState currentGamePadState;
  44. GamePadState lastGamePadState;
  45. // the following constants control the speed at which the camera moves
  46. // how fast does the camera move up, down, left, and right?
  47. const float CameraRotateSpeed = .1f;
  48. // how fast does the camera zoom in and out?
  49. const float CameraZoomSpeed = .01f;
  50. // the camera can't be further away than this distance
  51. const float CameraMaxDistance = 10.0f;
  52. // and it can't be closer than this
  53. const float CameraMinDistance = 2.0f;
  54. // the following constants control the camera's default position
  55. const float CameraDefaultArc = 30.0f;
  56. const float CameraDefaultRotation = 0;
  57. const float CameraDefaultDistance = 3.0f;
  58. // Camera control values
  59. float cameraArc = CameraDefaultArc;
  60. float cameraRotation = CameraDefaultRotation;
  61. float cameraDistance = CameraDefaultDistance;
  62. #endregion
  63. #region Initialization
  64. /// <summary>
  65. /// Constructor.
  66. /// </summary>
  67. public ObjectPlacementOnAvatarGame()
  68. {
  69. graphics = new GraphicsDeviceManager(this);
  70. Content.RootDirectory = "Content";
  71. graphics.PreferredBackBufferWidth = 1280;
  72. graphics.PreferredBackBufferHeight = 720;
  73. graphics.PreferMultiSampling = true;
  74. // Avatars require GamerServices
  75. Components.Add(new GamerServicesComponent(this));
  76. }
  77. /// <summary>
  78. /// Load your graphics content.
  79. /// </summary>
  80. protected override void LoadContent()
  81. {
  82. // Create random avatar description and load the renderer and animation
  83. avatarDescription = AvatarDescription.CreateRandom();
  84. avatarRenderer = new AvatarRenderer(avatarDescription);
  85. // Load 4 of the preset animations
  86. avatarAnimations[0] = new AvatarAnimation(AvatarAnimationPreset.Stand0);
  87. avatarAnimations[1] = new AvatarAnimation(AvatarAnimationPreset.Celebrate);
  88. avatarAnimations[2] = new AvatarAnimation(AvatarAnimationPreset.Clap);
  89. avatarAnimations[3] = new AvatarAnimation(AvatarAnimationPreset.Stand5);
  90. // Current animation to play and update
  91. currentAvatarAnimation = avatarAnimations[0];
  92. // Load the baseball bat model
  93. baseballBat = Content.Load<Model>("baseballbat");
  94. // Initialize the rendering matrices
  95. world = Matrix.CreateRotationY(MathHelper.Pi);
  96. projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
  97. GraphicsDevice.Viewport.AspectRatio,
  98. .01f, 200.0f);
  99. // Initialize the list of bones in world space
  100. bonesWorldSpace = new List<Matrix>(AvatarRenderer.BoneCount);
  101. for (int i = 0; i < AvatarRenderer.BoneCount; i++)
  102. bonesWorldSpace.Add(Matrix.Identity);
  103. }
  104. #endregion
  105. #region Update and Draw
  106. /// <summary>
  107. /// Allows the game to run logic.
  108. /// </summary>
  109. protected override void Update(GameTime gameTime)
  110. {
  111. HandleInput();
  112. UpdateCamera(gameTime);
  113. // Set avatar rendering matrices
  114. avatarRenderer.World = world;
  115. avatarRenderer.View = view;
  116. avatarRenderer.Projection = projection;
  117. // Update the current animation and world space bones
  118. if (avatarRenderer.State == AvatarRendererState.Ready)
  119. {
  120. currentAvatarAnimation.Update(gameTime.ElapsedGameTime, true);
  121. BonesToWorldSpace(avatarRenderer,
  122. currentAvatarAnimation,
  123. bonesWorldSpace);
  124. }
  125. base.Update(gameTime);
  126. }
  127. /// <summary>
  128. /// This is called when the game should draw itself.
  129. /// </summary>
  130. protected override void Draw(GameTime gameTime)
  131. {
  132. GraphicsDevice.Clear(Color.CornflowerBlue);
  133. DrawBaseballBat();
  134. avatarRenderer.Draw(currentAvatarAnimation.BoneTransforms,
  135. currentAvatarAnimation.Expression);
  136. base.Draw(gameTime);
  137. }
  138. /// <summary>
  139. /// Draw the baseball bat
  140. /// </summary>
  141. private void DrawBaseballBat()
  142. {
  143. // Moves the bat closer to where we want it in the hand
  144. Matrix baseballBatOffset = Matrix.CreateRotationY(MathHelper.ToRadians(-20))
  145. * Matrix.CreateTranslation(0.01f, 0.05f, 0.0f);
  146. foreach (ModelMesh mesh in baseballBat.Meshes)
  147. {
  148. foreach (BasicEffect effect in mesh.Effects)
  149. {
  150. effect.EnableDefaultLighting();
  151. // Position the bat to be near the avatars right hand. The position
  152. // of the right special bone can be found by looking up the value in
  153. // our list of world space bones with the index of the bone we are
  154. // looking for. The bat is translated and rotated a small amount to
  155. // make it look better in the hand.
  156. effect.World = baseballBatOffset *
  157. bonesWorldSpace[(int)AvatarBone.SpecialRight];
  158. effect.View = view;
  159. effect.Projection = projection;
  160. }
  161. mesh.Draw();
  162. }
  163. }
  164. /// <summary>
  165. /// Updates a list of matrices to represent the location of the
  166. /// avatar bones in world space with the avatar animation applied.
  167. /// </summary>
  168. private static void BonesToWorldSpace(AvatarRenderer renderer, AvatarAnimation animation,
  169. List<Matrix> boneToUpdate)
  170. {
  171. // Bind pose of the avatar.
  172. // These positions are in local space, and are relative to the parent bone.
  173. IList<Matrix> bindPose = renderer.BindPose;
  174. // The current animation pose.
  175. // These positions are in local space, and are relative to the parent bone.
  176. IList<Matrix> animationPose = animation.BoneTransforms;
  177. // List of parent bones for each bone in the hierarchy
  178. IList<int> parentIndex = renderer.ParentBones;
  179. // Loop all of the bones.
  180. // Since the bone hierarchy is sorted by depth
  181. // we will transform the parent before any child.
  182. for (int i = 0; i < AvatarRenderer.BoneCount; i++)
  183. {
  184. // Find the transform of this bones parent.
  185. // If this is the first bone use the world matrix used on the avatar
  186. Matrix parentMatrix = (parentIndex[i] != -1)
  187. ? boneToUpdate[parentIndex[i]]
  188. : renderer.World;
  189. // Calculate this bones world space position
  190. boneToUpdate[i] = Matrix.Multiply(Matrix.Multiply(animationPose[i],
  191. bindPose[i]),
  192. parentMatrix);
  193. }
  194. }
  195. #endregion
  196. #region Input and Camera
  197. /// <summary>
  198. /// Handles input for quitting the game.
  199. /// </summary>
  200. void HandleInput()
  201. {
  202. lastGamePadState = currentGamePadState;
  203. currentGamePadState = GamePad.GetState(PlayerIndex.One);
  204. // Check for exit.
  205. if (currentGamePadState.Buttons.Back == ButtonState.Pressed)
  206. {
  207. Exit();
  208. }
  209. // Check to see if we should load another random avatar
  210. if (currentGamePadState.Buttons.RightShoulder == ButtonState.Pressed &&
  211. lastGamePadState.Buttons.RightShoulder != ButtonState.Pressed)
  212. {
  213. avatarDescription = AvatarDescription.CreateRandom();
  214. avatarRenderer = new AvatarRenderer(avatarDescription);
  215. }
  216. // Check to see if we need to play another animation
  217. if (currentGamePadState.Buttons.A == ButtonState.Pressed &&
  218. lastGamePadState.Buttons.A != ButtonState.Pressed)
  219. {
  220. currentAvatarAnimation = avatarAnimations[1];
  221. currentAvatarAnimation.CurrentPosition = TimeSpan.Zero;
  222. }
  223. else if (currentGamePadState.Buttons.B == ButtonState.Pressed &&
  224. lastGamePadState.Buttons.B != ButtonState.Pressed)
  225. {
  226. currentAvatarAnimation = avatarAnimations[2];
  227. currentAvatarAnimation.CurrentPosition = TimeSpan.Zero;
  228. }
  229. else if (currentGamePadState.Buttons.X == ButtonState.Pressed &&
  230. lastGamePadState.Buttons.X != ButtonState.Pressed)
  231. {
  232. currentAvatarAnimation = avatarAnimations[3];
  233. currentAvatarAnimation.CurrentPosition = TimeSpan.Zero;
  234. }
  235. else if (currentGamePadState.Buttons.Y == ButtonState.Pressed &&
  236. lastGamePadState.Buttons.Y != ButtonState.Pressed)
  237. {
  238. currentAvatarAnimation = avatarAnimations[0];
  239. currentAvatarAnimation.CurrentPosition = TimeSpan.Zero;
  240. }
  241. }
  242. /// <summary>
  243. /// Handles input for moving the camera.
  244. /// </summary>
  245. void UpdateCamera(GameTime gameTime)
  246. {
  247. float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
  248. // should we reset the camera?
  249. if (currentGamePadState.Buttons.RightStick == ButtonState.Pressed)
  250. {
  251. cameraArc = CameraDefaultArc;
  252. cameraDistance = CameraDefaultDistance;
  253. cameraRotation = CameraDefaultRotation;
  254. }
  255. // Check for input to rotate the camera up and down around the model.
  256. cameraArc += currentGamePadState.ThumbSticks.Right.Y * time *
  257. CameraRotateSpeed;
  258. // Limit the arc movement.
  259. cameraArc = MathHelper.Clamp(cameraArc, -90.0f, 90.0f);
  260. // Check for input to rotate the camera around the model.
  261. cameraRotation += currentGamePadState.ThumbSticks.Right.X * time *
  262. CameraRotateSpeed;
  263. // Check for input to zoom camera in and out.
  264. cameraDistance += currentGamePadState.Triggers.Left * time
  265. * CameraZoomSpeed;
  266. cameraDistance -= currentGamePadState.Triggers.Right * time
  267. * CameraZoomSpeed;
  268. // clamp the camera distance so it doesn't get too close or too far away.
  269. cameraDistance = MathHelper.Clamp(cameraDistance,
  270. CameraMinDistance, CameraMaxDistance);
  271. Matrix unrotatedView = Matrix.CreateLookAt(
  272. new Vector3(0, 0, cameraDistance), new Vector3(0, 1, 0), Vector3.Up);
  273. view = Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
  274. Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
  275. unrotatedView;
  276. }
  277. #endregion
  278. }
  279. #region Entry Point
  280. /// <summary>
  281. /// The main entry point for the application.
  282. /// </summary>
  283. static class Program
  284. {
  285. static void Main()
  286. {
  287. using (ObjectPlacementOnAvatarGame game = new ObjectPlacementOnAvatarGame())
  288. {
  289. game.Run();
  290. }
  291. }
  292. }
  293. #endregion
  294. }