using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Input; namespace MorphShader { public class MorphingModel { #region Fields public enum State { MORPHING, STATIC } private int currentLevel; private int morphLevel; private Model[] models; private Texture2D[] textures; private int levelCount; private string modelPath; private State morphState; private ContentManager content; private GraphicsDevice graphics; private Effect staticEffect; private Effect morphingEffect; EffectTechnique technique; EffectPass pass; Matrix world; Matrix view; Matrix projection; Quaternion rotation = Quaternion.Identity; float yaw = 0.0f; float pitch = 0.0f; private const float MORPH_TIME = 1.0f; // Morphing time in seconds private float morphInterval; private VertexDeclaration dualPositionTextureVertexDeclaration; bool morphing = true; KeyboardState prevKeyboard; KeyboardState keyboardState; #endregion #region Initialization public MorphingModel(int levelCount, string modelPath, ContentManager content, GraphicsDevice graphics) { this.levelCount = levelCount; this.modelPath = modelPath; this.content = content; this.graphics = graphics; Initialize(); LoadContent(); } public void LoadContent() { models = new Model[levelCount]; textures = new Texture2D[levelCount]; for (int i = 0; i < levelCount; i++) { models[i] = content.Load(modelPath + i.ToString()); textures[i] = ((BasicEffect)models[i].Meshes[0].MeshParts[0].Effect).Texture; } staticEffect = content.Load("Static"); morphingEffect = content.Load("Morph"); } public void Initialize() { currentLevel = 0; morphLevel = 0; morphState = State.STATIC; dualPositionTextureVertexDeclaration = new VertexDeclaration(graphics, Vertex2PositionNormalTexture.VertexElements); world = Matrix.Identity; view = Matrix.CreateLookAt(new Vector3(0, 100, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 1)); projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, graphics.Viewport.AspectRatio, 1f, 10000.0f); } #endregion #region Update public void Update(GameTime gameTime) { prevKeyboard = keyboardState; keyboardState = Keyboard.GetState(); GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); //The next 4 if statements rotate the model. if (keyboardState.IsKeyDown(Keys.W)) { yaw -= .01f; } if (keyboardState.IsKeyDown(Keys.S)) { yaw += .01f; } if (keyboardState.IsKeyDown(Keys.A)) { pitch -= .01f; } if (keyboardState.IsKeyDown(Keys.D)) { pitch += .01f; } //update rotation rotation = Quaternion.CreateFromYawPitchRoll(yaw, pitch, 0); if (keyboardState.IsKeyDown(Keys.Space) && prevKeyboard.IsKeyUp(Keys.Space)) { morphing = !morphing; } // First we check for the end of a morph if (morphInterval >= 1) { // We have reached the end of a morph! currentLevel = morphLevel; // Then we reset the interval and set the model to still morphInterval = 0; morphState = State.STATIC; } // Then we check if we still need to continue a morph if (morphState == State.MORPHING) { if (morphing) { // Advance the interval morphInterval += (float)gameTime.ElapsedGameTime.TotalSeconds / MORPH_TIME; } } // Then we check to see if a new morph has started, only if we are in a static state if (morphState == State.STATIC) { if ((keyboardState.IsKeyDown(Keys.Down) || gamePadState.IsButtonDown(Buttons.DPadDown)) && currentLevel > 0) { morphState = State.MORPHING; morphLevel = currentLevel - 1; } if ((keyboardState.IsKeyDown(Keys.Up) || gamePadState.IsButtonDown(Buttons.DPadUp)) && currentLevel < levelCount - 1) { morphState = State.MORPHING; morphLevel = currentLevel + 1; } } } #endregion #region Draw public void Draw() { graphics.Clear(Color.CornflowerBlue); if (morphState == State.STATIC) { DrawByBufferStatic(); } else { DrawByBufferMorphing(); } } private void DrawByBufferStatic() { graphics.Clear(Color.CornflowerBlue); technique = staticEffect.Techniques[0]; pass = technique.Passes[0]; // Draw the model. A model can have multiple meshes, so loop. for (int m = 0; m < models[currentLevel].Meshes.Count; m++) { ModelMesh mesh = models[currentLevel].Meshes[m]; for (int p = 0; p < mesh.MeshParts.Count; p++) { ModelMeshPart meshPart = models[currentLevel].Meshes[m].MeshParts[p]; graphics.VertexDeclaration = meshPart.VertexDeclaration; graphics.Vertices[0].SetSource(mesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride); graphics.Indices = mesh.IndexBuffer; staticEffect.Parameters["World"].SetValue(Matrix.CreateFromQuaternion(rotation)); staticEffect.Parameters["Projection"].SetValue(projection); staticEffect.Parameters["View"].SetValue(view); staticEffect.Parameters["Texture"].SetValue(textures[currentLevel]); staticEffect.Begin(); pass.Begin(); graphics.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, meshPart.NumVertices, 0, meshPart.PrimitiveCount); pass.End(); staticEffect.End(); } } } private void DrawByBufferMorphing() { graphics.Clear(Color.CornflowerBlue); technique = morphingEffect.Techniques[0]; pass = technique.Passes[0]; // Draw the model. A model can have multiple meshes, so loop. for (int m = 0; m < models[currentLevel].Meshes.Count; m++) { ModelMesh sourceMesh = models[currentLevel].Meshes[m]; ModelMesh targetMesh = models[morphLevel].Meshes[m]; for (int p = 0; p < sourceMesh.MeshParts.Count; p++) { ModelMeshPart sourceMeshPart = models[currentLevel].Meshes[m].MeshParts[p]; ModelMeshPart targetMeshPart = models[morphLevel].Meshes[m].MeshParts[p]; graphics.VertexDeclaration = dualPositionTextureVertexDeclaration; graphics.Vertices[0].SetSource(sourceMesh.VertexBuffer, sourceMeshPart.StreamOffset, sourceMeshPart.VertexStride); graphics.Vertices[1].SetSource(MakeBufferPlusOne(targetMesh.VertexBuffer), targetMeshPart.StreamOffset, targetMeshPart.VertexStride); graphics.Indices = sourceMesh.IndexBuffer; //morphingEffect.Parameters["World"].SetValue(world); morphingEffect.Parameters["World"].SetValue(Matrix.CreateFromQuaternion(rotation)); morphingEffect.Parameters["Projection"].SetValue(projection); morphingEffect.Parameters["View"].SetValue(view); morphingEffect.Parameters["Interval"].SetValue(morphInterval); morphingEffect.Parameters["Texture"].SetValue(textures[currentLevel]); morphingEffect.Begin(); pass.Begin(); graphics.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, sourceMeshPart.NumVertices, 0, sourceMeshPart.PrimitiveCount); pass.End(); morphingEffect.End(); } } } #endregion #region Methods private VertexBuffer MakeBufferPlusOne(VertexBuffer oldBuffer) { int sizeOfNewBufferInBytes = oldBuffer.SizeInBytes / 32; VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[sizeOfNewBufferInBytes]; oldBuffer.GetData(vertices); VertexBuffer newBuffer = new VertexBuffer(graphics, (sizeOfNewBufferInBytes * 32) + 32, BufferUsage.WriteOnly); VertexPositionNormalTexture[] newVertices = new VertexPositionNormalTexture[sizeOfNewBufferInBytes + 1]; for (int i = 0; i < newVertices.Length - 1; i++) { if (i == 0) { newVertices[i] = vertices[0]; } newVertices[i + 1] = vertices[i]; } newBuffer.SetData(newVertices); return newBuffer; } #endregion } }