Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using Microsoft.Xna.Framework;
- using Microsoft.Xna.Framework.Audio;
- using Microsoft.Xna.Framework.Content;
- using Microsoft.Xna.Framework.GamerServices;
- using Microsoft.Xna.Framework.Graphics;
- using Microsoft.Xna.Framework.Input;
- using Microsoft.Xna.Framework.Media;
- using Microsoft.Xna.Framework.Net;
- using Microsoft.Xna.Framework.Storage;
- namespace MorphTargetAnimation
- {
- /// <summary>
- /// This class presents an approach to morph target animation.
- /// A custom vertex declaration is used to blend multiple vertex streams
- /// so that multiple model geometry can be interpolated. Vertex streams
- /// are then blended in an HLSL shader.
- ///
- /// This approach requires model topology to be exactly the same for all
- /// morph targets. Vertex/Index counts and orders must be the same as
- /// well as the number and order of meshes contained within each model.
- /// This code makes the assumption that the predescribed requirements
- /// are met and will exhibit undesired behaviour/thrown exceptions if
- /// provided with differing topologies.
- ///
- /// Capability for further extension:
- ///
- /// Currently this implementation only uses 2 vertex streams, so only
- /// two morph targets can be blended at any one time. This implementation
- /// could be further improved by allowing varying interpolation with
- /// other morph targets. The vertex stream limit on DX9 cards I believe
- /// is 16 streams, however utilisation of that many streams is unlikely
- /// to be required. Instead, I'd recommend a lower limit of streams, using
- /// the first stream as a neutral expression (in the case of facial
- /// animation) and blending to other streams (in order of importance.)
- /// As the strength of a morph target is increased, it can increase in
- /// importance. This way the least visible (and therefore least
- /// important) streams can be kept outside the enforced stream limit.
- ///
- /// The morph targets could also be generated into vertex textures
- /// and VTF used to change positions and normals.
- /// </summary>
- public class MorphGameObject : DrawableGameComponent
- {
- #region Fields
- private ContentManager Content;
- private VertexDeclaration morphingVertexDeclaration;
- private Effect morphingEffect;
- // Models to blend between
- private Model[] models;
- // Interpolation value for blending between models
- private float tween;
- // Camera matrices
- Matrix world, view, projection;
- // Camera view
- float cameraArc = 0;
- float cameraRotation = MathHelper.Pi;
- float cameraDistance = 300;
- // Which mesh is currently being blended from
- int primaryMeshIndex;
- // Track input to detect input changes
- KeyboardState lastKeyboardState;
- GamePadState lastGamePadState;
- #endregion
- #region Properties
- public float Tween
- {
- get { return tween; }
- set { tween = value; }
- }
- public int PrimaryMeshIndex
- {
- get { return primaryMeshIndex; }
- set { primaryMeshIndex = value; }
- }
- #endregion
- public MorphGameObject(Game game, ContentManager Content)
- : base(game)
- {
- this.Content = Content;
- }
- public override void Initialize()
- {
- base.Initialize();
- // Initialise the custom vertex declaration to allow two vertex streams to be blended
- morphingVertexDeclaration = new VertexDeclaration(GraphicsDevice,
- new VertexElement[] {
- new VertexElement(
- // Texture coordinates from vertex stream 0
- 0, // which stream this element comes from
- sizeof(float) * 6, // byte offset within the stream
- VertexElementFormat.Vector2,
- VertexElementMethod.Default,
- VertexElementUsage.TextureCoordinate,
- 0
- ),
- // Positions from vertex stream 0
- new VertexElement(
- 0, // which stream this element comes from
- 0, // byte offset within the stream
- VertexElementFormat.Vector3,
- VertexElementMethod.Default,
- VertexElementUsage.Position,
- 0
- ),
- // Normals from vertex stream 0
- new VertexElement(
- 0, // which stream this element comes from
- sizeof(float) * 3, // byte offset within the stream
- VertexElementFormat.Vector3,
- VertexElementMethod.Default,
- VertexElementUsage.Normal,
- 0
- ),
- // Position from vertex stream 1
- new VertexElement(
- 1, // which stream this element comes from
- 0, // byte offset within the stream
- VertexElementFormat.Vector3,
- VertexElementMethod.Default,
- VertexElementUsage.Position,
- 1
- ),
- // Normals from vertex stream 1
- new VertexElement(
- 1, // which stream this element comes from
- sizeof(float) * 3, // byte offset within the stream
- VertexElementFormat.Vector3,
- VertexElementMethod.Default,
- VertexElementUsage.Normal,
- 1
- )
- }
- );
- // Set up the camera
- world = Matrix.Identity;
- view = Matrix.CreateLookAt(new Vector3(300, 100, 300), Vector3.Zero, Vector3.Up);
- projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), GraphicsDevice.Viewport.AspectRatio, 0.1f, 1000.0f);
- }
- protected override void LoadContent()
- {
- // Load the models and morphing effect
- models = new Model[3];
- for (int i = 0; i < models.Length; i++)
- {
- models[i] = Content.Load<Model>((i + 1).ToString());
- }
- morphingEffect = Content.Load<Effect>("MorphingEffect");
- base.LoadContent();
- }
- /// <summary>
- /// Handles input.
- /// </summary>
- private void HandleInput(GameTime gameTime)
- {
- KeyboardState currentKeyboardState = Keyboard.GetState();
- GamePadState currentGamePadState = GamePad.GetState(PlayerIndex.One);
- float time = 20 * (float)gameTime.ElapsedGameTime.TotalSeconds;
- // Check for input to rotate the camera up and down around the model.
- if (currentKeyboardState.IsKeyDown(Keys.Up) ||
- currentKeyboardState.IsKeyDown(Keys.W))
- {
- cameraArc += time * 0.1f;
- }
- if (currentKeyboardState.IsKeyDown(Keys.Down) ||
- currentKeyboardState.IsKeyDown(Keys.S))
- {
- cameraArc -= time * 0.1f;
- }
- cameraArc += currentGamePadState.ThumbSticks.Right.Y * time * 0.25f;
- // Limit the arc movement.
- if (cameraArc > 90.0f)
- cameraArc = 90.0f;
- else if (cameraArc < -90.0f)
- cameraArc = -90.0f;
- // Check for input to rotate the camera around the model.
- if (currentKeyboardState.IsKeyDown(Keys.Right) ||
- currentKeyboardState.IsKeyDown(Keys.D))
- {
- cameraRotation += time * 0.1f;
- }
- if (currentKeyboardState.IsKeyDown(Keys.Left) ||
- currentKeyboardState.IsKeyDown(Keys.A))
- {
- cameraRotation -= time * 0.1f;
- }
- cameraRotation += currentGamePadState.ThumbSticks.Right.X * time * 0.25f;
- // Check for input to zoom camera in and out.
- if (currentKeyboardState.IsKeyDown(Keys.Z))
- cameraDistance += time * 2.5f;
- if (currentKeyboardState.IsKeyDown(Keys.X))
- cameraDistance -= time * 2.5f;
- cameraDistance += currentGamePadState.Triggers.Left * time * 5f;
- cameraDistance -= currentGamePadState.Triggers.Right * time * 5f;
- // Limit the camera distance.
- if (cameraDistance > 500.0f)
- cameraDistance = 500.0f;
- else if (cameraDistance < 10.0f)
- cameraDistance = 10.0f;
- // Toggle wireframe
- if ((lastGamePadState.IsButtonDown(Buttons.A) && currentGamePadState.IsButtonUp(Buttons.A))
- || (lastKeyboardState.IsKeyDown(Keys.Q) && currentKeyboardState.IsKeyUp(Keys.Q)))
- GraphicsDevice.RenderState.FillMode = (GraphicsDevice.RenderState.FillMode == FillMode.Solid) ? FillMode.WireFrame : FillMode.Solid;
- lastGamePadState = currentGamePadState;
- lastKeyboardState = currentKeyboardState;
- }
- public override void Update(GameTime gameTime)
- {
- // Increase the tween value over time, while maintaining frame rate independence
- tween += (float)gameTime.ElapsedGameTime.TotalSeconds;
- // Clean up the tween value to be between -180 and 180 degrees
- tween = MathHelper.WrapAngle(tween);
- if (tween > 1)
- {
- tween -= 1;
- primaryMeshIndex++;
- if (primaryMeshIndex >= models.Length)
- primaryMeshIndex = 0;
- }
- HandleInput(gameTime);
- base.Update(gameTime);
- }
- public override void Draw(GameTime gameTime)
- {
- // Ensure that the device vertex declaration is set correctly
- GraphicsDevice.VertexDeclaration = morphingVertexDeclaration;
- // Update view matrix
- view =
- Matrix.CreateRotationY(cameraRotation) *
- Matrix.CreateRotationX(cameraArc) *
- Matrix.CreateLookAt(new Vector3(0, 0, -cameraDistance),
- new Vector3(0, 0, 0), Vector3.Up);
- // Set the camera settings
- morphingEffect.Parameters["World"].SetValue(world);
- morphingEffect.Parameters["View"].SetValue(view);
- morphingEffect.Parameters["Projection"].SetValue(projection);
- // Set the tween value to follow a sine curve with an output value between 0 and 1
- morphingEffect.Parameters["TweenValue"].SetValue(tween);
- DrawHair();
- // Loop through each of the meshes
- for (int i = 0; i < models[0].Meshes.Count; i++)
- {
- if (models[0].Meshes[i].Name == "Mesh_001")
- continue;
- int numberOfPrimitives = (models[0].Meshes[i].VertexBuffer.SizeInBytes / VertexPositionNormalTexture.SizeInBytes) / 3;
- // Mesh topology must be the same, therefore the first model's indices should be correct for all
- GraphicsDevice.Indices = models[0].Meshes[i].IndexBuffer;
- // Set a vertex element stride of 4 bytes
- int stride = 32;
- // Calculate the index of the model being morphed to, wrapping back to start if necessary.
- int nextIndex = primaryMeshIndex + 1;
- if (nextIndex >= models.Length)
- nextIndex = 0;
- // Set the vertex streams from the models, this must conform to the custom vertex declaration
- GraphicsDevice.Vertices[0].SetSource(models[primaryMeshIndex].Meshes[i].VertexBuffer, 0, stride);
- GraphicsDevice.Vertices[1].SetSource(models[nextIndex].Meshes[i].VertexBuffer, 0, stride);
- // The default model content processor supplies the default mesh effect with its texture
- // automatically. Set the custom effect's texture appropiately from the default effect
- BasicEffect basicEffect = (BasicEffect)(models[0].Meshes[i].Effects[0]);
- morphingEffect.Parameters["Texture"].SetValue(basicEffect.Texture);
- // Draw the geometry using the morphing effect
- morphingEffect.Begin();
- {
- foreach (EffectPass pass in morphingEffect.CurrentTechnique.Passes)
- {
- pass.Begin();
- {
- GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, numberOfPrimitives * 3 * 6, 0, numberOfPrimitives * 6);
- }
- pass.End();
- }
- }
- morphingEffect.End();
- }
- base.Draw(gameTime);
- }
- void DrawHair()
- {
- // Draw the model. A model can have multiple meshes, so loop.
- foreach (ModelMesh mesh in models[0].Meshes)
- {
- if (mesh.Name != "Mesh_001")
- continue;
- // This is where the mesh orientation is set, as well as our camera and projection.
- foreach (BasicEffect effect in mesh.Effects)
- {
- effect.EnableDefaultLighting();
- effect.World = world;
- effect.View = view;
- effect.Projection = projection;
- }
- // Draw the mesh, using the effects set above.
- mesh.Draw();
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement