//--------------------------------------------------------------------------------------------------------------------------------------------------- // <copyright file="Draw.cs" company="DarkWynter Studios"> // Copyright (C)2007 DarkWynter Studios. All rights reserved. // </copyright> //--------------------------------------------------------------------------------------------------------------------------------------------------- // {Contact : darkwynter.com for licensing information //--------------------------------------------------------------------------------------------------------------------------------------------------- namespace DarkWynter.Stream { #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Xml; using Xclna.Xna.Animation.Content; using Xclna.Xna.Animation; #endregion /// <summary> /// Vertex data containing Position and Normal info /// </summary> public struct VertexPositionNormal { /// <summary> /// Position /// </summary> public Vector3 Position; /// <summary> /// Normal /// </summary> public Vector3 Normal; } /// <summary> /// Used to pass instance data to shaders through existing gpu pipelines. /// /// World matrix values are stored as follows: /// Fog = World.M11, World.M12, World.M13, World.M14 /// Binormal = World.M21, World.M22, World.M23, World.M24 /// Tangent = World.M31, World.M32, World.M33, World.M34 /// Depth = World.M41, World.M42, World.M43, World.M44 /// </summary> public struct VertexFogBinormalTangentDepth { /// <summary> /// World.M11, World.M12, World.M13, World.M14 /// </summary> public Vector4 Fog; /// <summary> /// World.M21, World.M22, World.M23, World.M24 /// </summary> public Vector4 Binormal; /// <summary> /// World.M31, World.M32, World.M33, World.M34 /// </summary> public Vector4 Tangent; /// <summary> /// World.M41, World.M42, World.M43, World.M44 /// </summary> public Vector4 Depth; } /// <summary> /// Stores all the draw function calls for game objects /// </summary> public class Draw { private Model _model; private ModelAnimator _animator; private Matrix _matrix; private List<Texture2D> _textureList = new List<Texture2D>(); private Matrix _initialTransform = Matrix.Identity; private VertexBuffer _vertexBuffer; private VertexDeclaration _vertexDeclaration; private VertexFogBinormalTangentDepth[] _instanceDataFBTD; private Enums_Stream.DrawMethod _drawMethod; private string _technique; /// <summary> /// Model used by this GameObject. /// </summary> public Model model { get { return _model; } set { _model = value; } } /// <summary> /// Model animator used by this GameObject. /// </summary> public ModelAnimator animator { get { return _animator; } set { _animator = value; } } /// <summary> /// Location and orientation matrix passed to shaders. /// </summary> public Matrix matrix { get { return _matrix; } set { _matrix = value; } } /// <summary> /// Textures associated with this object. /// </summary> public List<Texture2D> textureList { get { return _textureList; } set { _textureList = value; } } /// <summary> /// Needed to fix the rotation caused to animated models /// </summary> public Matrix initialTransform { get { return _initialTransform; } set { _initialTransform = value; } } /// <summary> /// Buffer to store vertices /// </summary> public VertexBuffer vertexBuffer { get { return _vertexBuffer; } set { _vertexBuffer = value; } } /// <summary> /// Vertex Declaration. /// </summary> public VertexDeclaration vertexDeclaration { get { return _vertexDeclaration; } set { _vertexDeclaration = value; } } /// <summary> /// Instance data (Fog, Binormal, Tangent, Depth) /// Used for GameObject instancing /// </summary> public VertexFogBinormalTangentDepth[] instanceDataFBTD { get { return _instanceDataFBTD; } set { _instanceDataFBTD = value; } } /// <summary> /// Usage of the draw object /// </summary> public Enums_Stream.DrawMethod drawMethod { get { return _drawMethod; } set { _drawMethod = value; } } /// <summary> /// Shader technique to use /// </summary> public string technique { get { return _technique; } set { _technique = value; } } private GraphicsDevice gd; #region Stream formats for instancing /// <summary> /// Tells the GPU how to format the streams for instancing /// Uses the Fog, Binormal, Tangent and Depth fields of the stream /// </summary> public VertexElement[] FBTDElements = new VertexElement[] { new VertexElement(0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0), new VertexElement(0, sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0), new VertexElement(0, sizeof(float) * 6, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0), new VertexElement(1, 0, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 1), new VertexElement(1, sizeof(float) * 4, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 2), new VertexElement(1, sizeof(float) * 8, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 3), new VertexElement(1, sizeof(float) * 12, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 4), }; /// <summary> /// Tells the GPU how to format the streams for instancing /// Used for instanced animation /// </summary> public VertexElement[] InstAnimElements = new VertexElement[] { new VertexElement(0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0), new VertexElement(0, sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0), new VertexElement(0, sizeof(float) * 6, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0), new VertexElement(0, sizeof(float) * 8, VertexElementFormat.Byte4, VertexElementMethod.Default, VertexElementUsage.BlendIndices, 0), new VertexElement(0, sizeof(float) * 9, VertexElementFormat.Color, VertexElementMethod.Default, VertexElementUsage.BlendWeight, 0), new VertexElement(1, 0, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 1), new VertexElement(1, sizeof(float) * 4, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 2), new VertexElement(1, sizeof(float) * 8, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 3), new VertexElement(1, sizeof(float) * 12, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 4), }; #endregion /// <summary> /// Constructor /// </summary> public Draw() { gd = Statics_Stream.RenderSettings.graphics.GraphicsDevice; } /// <summary> /// Constructor /// </summary> /// <param name="method">The draw method to use</param> /// <param name="drawTechnique">Technique to use</param> public Draw(Enums_Stream.DrawMethod method, string drawTechnique) { gd = Statics_Stream.RenderSettings.graphics.GraphicsDevice; drawMethod = method; technique = drawTechnique; } /// <summary> /// Base draw function which calls the required functions depending on the draw method of this object /// </summary> public void DoDraw() { switch (drawMethod) { case Enums_Stream.DrawMethod.Terrain_Draw: { DrawTerrain(); break; } case Enums_Stream.DrawMethod.BasicGameObject_Draw: { DrawBasicGO(); break; } case Enums_Stream.DrawMethod.PropList_Draw: { DrawPropList(); break; } case Enums_Stream.DrawMethod.PropListAnimated_Draw: { DrawAnimatedPropList(); break; } case Enums_Stream.DrawMethod.BillBoards_Draw: { DrawBillBoards(); break; } case Enums_Stream.DrawMethod.GPUObjectList_Draw: { DrawGPUObjectList(); break; } case Enums_Stream.DrawMethod.Shield_Draw: { DrawShield(); break; } case Enums_Stream.DrawMethod.Animated_Draw: { DrawAnimation(); break; } } } private void DrawTerrain() { vertexBuffer = new VertexBuffer(gd, typeof(VertexFogBinormalTangentDepth), instanceDataFBTD.Length, BufferUsage.WriteOnly); vertexBuffer.SetData<VertexFogBinormalTangentDepth>(instanceDataFBTD); // Set the vertex declaration gd.VertexDeclaration = vertexDeclaration; gd.Vertices[0].SetFrequencyOfIndexData(instanceDataFBTD.Length); // Tell the GPU how many times to run through the instance data and set the stream. gd.Vertices[1].SetSource(vertexBuffer, 0, gd.VertexDeclaration.GetVertexStrideSize(1)); gd.Vertices[1].SetFrequencyOfInstanceData(1); InstancedTriStrips(); } private void DrawBasicGO() { ShaderParameters.DrawFX.World.SetValue(matrix); ShaderParameters.DrawFX.modelTexture1.SetValue(textureList[0]); ShaderParameters.DrawFX.bumpTexture1.SetValue(textureList[1]); // Draw the model TriStrips(); } private void DrawPropList() { ShaderParameters.DrawFX.modelTexture1.SetValue(textureList[0]); ShaderParameters.DrawFX.bumpTexture1.SetValue(textureList[1]); // Set the vertex declaration gd.VertexDeclaration = vertexDeclaration; gd.Vertices[0].SetFrequencyOfIndexData(instanceDataFBTD.Length); // Tell the GPU how many times to run through the instance data and set the stream. gd.Vertices[1].SetSource(vertexBuffer, 0, gd.VertexDeclaration.GetVertexStrideSize(1)); gd.Vertices[1].SetFrequencyOfInstanceData(1); InstancedTriStrips(); } private void DrawAnimatedPropList() { ShaderParameters.DrawFX.modelTexture1.SetValue(textureList[0]); ShaderParameters.DrawFX.bumpTexture1.SetValue(textureList[1]); // Set the vertex declaration gd.VertexDeclaration = vertexDeclaration; gd.Vertices[0].SetFrequencyOfIndexData(instanceDataFBTD.Length); // Tell the GPU how many times to run through the instance data and set the stream. gd.Vertices[1].SetSource(vertexBuffer, 0, gd.VertexDeclaration.GetVertexStrideSize(1)); gd.Vertices[1].SetFrequencyOfInstanceData(1); animator.world = matrix; animator.Update(); InstancedAnimation(); } private void DrawBillBoards() { // Set Shader Params ShaderParameters.DrawFX.World.SetValue(matrix); // Set Textures ShaderParameters.DrawFX.modelTexture1.SetValue(textureList[0]); //ShaderParameters.DrawFX.bumpTexture1.SetValue(billboardList[i].texture); ShaderParameters.DrawFX.ViewProj.SetValue(Statics_Stream.RenderSettings.matrixView * Statics_Stream.RenderSettings.matrixProjection); TriStrips(); } private void DrawGPUObjectList() { ShaderParameters.DrawFX.modelTexture1.SetValue(textureList[0]); ShaderParameters.DrawFX.bumpTexture1.SetValue(textureList[1]); // Set the vertex declaration gd.VertexDeclaration = vertexDeclaration; gd.Vertices[0].SetFrequencyOfIndexData(instanceDataFBTD.Length); // Tell GPU how many times to run through the instance data and set the stream. gd.Vertices[1].SetSource(vertexBuffer, 0, gd.VertexDeclaration.GetVertexStrideSize(1)); gd.Vertices[1].SetFrequencyOfInstanceData(1); // Draw the model InstancedTriStrips(); } private void DrawShield() { ShaderParameters.DrawFX.World.SetValue(matrix); TriStrips(); } private void DrawAnimation() { ShaderParameters.DrawFX.World.SetValue(matrix); ShaderParameters.DrawFX.modelTexture1.SetValue(textureList[0]); ShaderParameters.DrawFX.bumpTexture1.SetValue(textureList[1]); animator.world = matrix; animator.Update(); Animation(); } /// <summary> /// Draws a stand alone model with no instancing or triangle stripping. /// </summary> private void TriList() { foreach (ModelMesh mesh in model.Meshes) { gd.Indices = mesh.IndexBuffer; foreach (ModelMeshPart part in mesh.MeshParts) { gd.VertexDeclaration = part.VertexDeclaration; gd.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride); part.Effect.CurrentTechnique = part.Effect.Techniques[technique]; part.Effect.Begin(); for (int k = 0; k < part.Effect.CurrentTechnique.Passes.Count; k++) { EffectPass pass = part.Effect.CurrentTechnique.Passes[k]; pass.Begin(); gd.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.BaseVertex, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount); pass.End(); } part.Effect.End(); } } } /// <summary> /// Draws a stand alone model that uses the triangle-strip content processor for loading. /// </summary> private void TriStrips() { foreach (ModelMesh mesh in model.Meshes) { gd.Indices = mesh.IndexBuffer; foreach (ModelMeshPart part in mesh.MeshParts) { part.Effect.CurrentTechnique = part.Effect.Techniques[technique]; gd.VertexDeclaration = part.VertexDeclaration; gd.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride); int numPrimitives; if (mesh.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) { numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(ushort); } else { numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(int); } numPrimitives -= 2; part.Effect.Begin(); foreach (EffectPass pass in part.Effect.CurrentTechnique.Passes) { pass.Begin(); gd.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, part.BaseVertex, 0, part.NumVertices, part.StartIndex, numPrimitives); pass.End(); } part.Effect.End(); } } } /// <summary> /// Draws an instanced model that uses the standard content processor. /// </summary> private void InstancedTriList() { foreach (ModelMesh mesh in model.Meshes) { // Set the index buffer gd.Indices = mesh.IndexBuffer; // Tell the GPU how many times to run through the vertex data and set the stream. gd.Vertices[0].SetSource(mesh.VertexBuffer, 0, gd.VertexDeclaration.GetVertexStrideSize(0)); foreach (ModelMeshPart part in mesh.MeshParts) { part.Effect.CurrentTechnique = part.Effect.Techniques[technique]; part.Effect.Begin(); for (int k = 0; k < part.Effect.CurrentTechnique.Passes.Count; k++) { EffectPass pass = part.Effect.CurrentTechnique.Passes[k]; pass.Begin(); gd.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.BaseVertex, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount); pass.End(); } part.Effect.End(); } } } /// <summary> /// Draws an instanced model that uses the triangle-strip content processor. /// </summary> private void InstancedTriStrips() { foreach (ModelMesh mesh in model.Meshes) { // Set the index buffer gd.Indices = mesh.IndexBuffer; int numPrimitives; if (mesh.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) { numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(ushort); } else { numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(int); } numPrimitives -= 2; // Tell the GPU how many times to run through the vertex data and set the stream. gd.Vertices[0].SetSource(mesh.VertexBuffer, 0, gd.VertexDeclaration.GetVertexStrideSize(0)); foreach (ModelMeshPart part in mesh.MeshParts) { part.Effect.CurrentTechnique = part.Effect.Techniques[technique]; part.Effect.Begin(); for (int k = 0; k < part.Effect.CurrentTechnique.Passes.Count; k++) { EffectPass pass = part.Effect.CurrentTechnique.Passes[k]; pass.Begin(); gd.RenderState.FillMode = Stream.Statics_Stream.RenderSettings.fillMode; gd.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, part.BaseVertex, 0, part.NumVertices, part.StartIndex, numPrimitives); pass.End(); } part.Effect.End(); } } } /// <summary> /// Draw the animated player model /// </summary> private void Animation() { try { int index = 0; // Update all the effects with the palette and world and draw the meshes for (int i = 0; i < animator.numMeshes; i++) { // Get the mesh ModelMesh mesh = model.Meshes[i]; // The starting index for the modelEffects array int effectStartIndex = index; if (animator.palette[i] != null && animator.matrixPaletteParams[index] != null) { foreach (Effect effect in mesh.Effects) { animator.worldParams[index].SetValue(matrix); animator.matrixPaletteParams[index].SetValue(animator.palette[i]); index++; } } else { foreach (Effect effect in mesh.Effects) { animator.worldParams[index].SetValue(animator.pose[mesh.ParentBone.Index] * matrix); index++; } } int numParts = mesh.MeshParts.Count; gd.Indices = mesh.IndexBuffer; for (int j = 0; j < numParts; j++) { ModelMeshPart currentPart = mesh.MeshParts[j]; if (currentPart.NumVertices == 0 || currentPart.PrimitiveCount == 0) continue; Effect currentEffect = animator.modelEffects[effectStartIndex + j]; currentEffect.CurrentTechnique = currentEffect.Techniques[technique]; gd.VertexDeclaration = currentPart.VertexDeclaration; gd.Vertices[0].SetSource(mesh.VertexBuffer, currentPart.StreamOffset, currentPart.VertexStride); currentEffect.Begin(); EffectPassCollection passes = currentEffect.CurrentTechnique.Passes; int numPasses = passes.Count; for (int k = 0; k < numPasses; k++) { EffectPass pass = passes[k]; pass.Begin(); gd.DrawIndexedPrimitives(PrimitiveType.TriangleList, currentPart.BaseVertex, 0, currentPart.NumVertices, currentPart.StartIndex, currentPart.PrimitiveCount); pass.End(); } currentEffect.End(); } } } catch (NullReferenceException) { throw new InvalidOperationException("The effects on the model for a " + "ModelAnimator were changed without calling ModelAnimator.InitializeEffectParams()."); } catch (InvalidCastException) { throw new InvalidCastException("ModelAnimator has thrown an InvalidCastException. This is " + "likely because the model uses too many bones for the matrix palette. The default palette size " + "is 56 for windows and 40 for Xbox."); } } /// <summary> /// Draw the instanced animated model /// </summary> private void InstancedAnimation() { try { int index = 0; // Update all the effects with the palette and world and draw the meshes for (int i = 0; i < animator.numMeshes; i++) { // Get the mesh ModelMesh mesh = model.Meshes[i]; // The starting index for the modelEffects array int effectStartIndex = index; if (animator.palette[i] != null && animator.matrixPaletteParams[index] != null) { foreach (Effect effect in mesh.Effects) { animator.worldParams[index].SetValue(matrix); animator.matrixPaletteParams[index].SetValue(animator.palette[i]); index++; } } else { foreach (Effect effect in mesh.Effects) { animator.worldParams[index].SetValue(animator.pose[mesh.ParentBone.Index] * matrix); index++; } } int numParts = mesh.MeshParts.Count; gd.Indices = mesh.IndexBuffer; for (int j = 0; j < numParts; j++) { ModelMeshPart currentPart = mesh.MeshParts[j]; if (currentPart.NumVertices == 0 || currentPart.PrimitiveCount == 0) continue; Effect currentEffect = animator.modelEffects[effectStartIndex + j]; currentEffect.CurrentTechnique = currentEffect.Techniques[technique]; //device.VertexDeclaration = currentPart.VertexDeclaration; gd.Vertices[0].SetSource(mesh.VertexBuffer, currentPart.StreamOffset, gd.VertexDeclaration.GetVertexStrideSize(0)); currentEffect.Begin(); EffectPassCollection passes = currentEffect.CurrentTechnique.Passes; int numPasses = passes.Count; for (int k = 0; k < numPasses; k++) { EffectPass pass = passes[k]; pass.Begin(); gd.DrawIndexedPrimitives(PrimitiveType.TriangleList, currentPart.BaseVertex, 0, currentPart.NumVertices, currentPart.StartIndex, currentPart.PrimitiveCount); pass.End(); } currentEffect.End(); } } } catch (NullReferenceException) { throw new InvalidOperationException("The effects on the model for a " + "ModelAnimator were changed without calling ModelAnimator.InitializeEffectParams()."); } catch (InvalidCastException) { throw new InvalidCastException("ModelAnimator has thrown an InvalidCastException. This is " + "likely because the model uses too many bones for the matrix palette. The default palette size " + "is 56 for windows and 40 for Xbox."); } } } }