//---------------------------------------------------------------------------------------------------------------------------------------------------
//
// Copyright (C)2007 DarkWynter Studios. All rights reserved.
//
//---------------------------------------------------------------------------------------------------------------------------------------------------
// {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
///
/// Vertex data containing Position and Normal info
///
public struct VertexPositionNormal
{
///
/// Position
///
public Vector3 Position;
///
/// Normal
///
public Vector3 Normal;
}
///
/// 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
///
public struct VertexFogBinormalTangentDepth
{
///
/// World.M11, World.M12, World.M13, World.M14
///
public Vector4 Fog;
///
/// World.M21, World.M22, World.M23, World.M24
///
public Vector4 Binormal;
///
/// World.M31, World.M32, World.M33, World.M34
///
public Vector4 Tangent;
///
/// World.M41, World.M42, World.M43, World.M44
///
public Vector4 Depth;
}
///
/// Stores all the draw function calls for game objects
///
public class Draw
{
private Model _model;
private ModelAnimator _animator;
private Matrix _matrix;
private List _textureList = new List();
private Matrix _initialTransform = Matrix.Identity;
private VertexBuffer _vertexBuffer;
private VertexDeclaration _vertexDeclaration;
private VertexFogBinormalTangentDepth[] _instanceDataFBTD;
private Enums_Stream.DrawMethod _drawMethod;
private string _technique;
///
/// Model used by this GameObject.
///
public Model model { get { return _model; } set { _model = value; } }
///
/// Model animator used by this GameObject.
///
public ModelAnimator animator { get { return _animator; } set { _animator = value; } }
///
/// Location and orientation matrix passed to shaders.
///
public Matrix matrix { get { return _matrix; } set { _matrix = value; } }
///
/// Textures associated with this object.
///
public List textureList { get { return _textureList; } set { _textureList = value; } }
///
/// Needed to fix the rotation caused to animated models
///
public Matrix initialTransform { get { return _initialTransform; } set { _initialTransform = value; } }
///
/// Buffer to store vertices
///
public VertexBuffer vertexBuffer { get { return _vertexBuffer; } set { _vertexBuffer = value; } }
///
/// Vertex Declaration.
///
public VertexDeclaration vertexDeclaration { get { return _vertexDeclaration; } set { _vertexDeclaration = value; } }
///
/// Instance data (Fog, Binormal, Tangent, Depth)
/// Used for GameObject instancing
///
public VertexFogBinormalTangentDepth[] instanceDataFBTD { get { return _instanceDataFBTD; } set { _instanceDataFBTD = value; } }
///
/// Usage of the draw object
///
public Enums_Stream.DrawMethod drawMethod { get { return _drawMethod; } set { _drawMethod = value; } }
///
/// Shader technique to use
///
public string technique { get { return _technique; } set { _technique = value; } }
private GraphicsDevice gd;
#region Stream formats for instancing
///
/// Tells the GPU how to format the streams for instancing
/// Uses the Fog, Binormal, Tangent and Depth fields of the stream
///
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),
};
///
/// Tells the GPU how to format the streams for instancing
/// Used for instanced animation
///
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
///
/// Constructor
///
public Draw()
{
gd = Statics_Stream.RenderSettings.graphics.GraphicsDevice;
}
///
/// Constructor
///
/// The draw method to use
/// Technique to use
public Draw(Enums_Stream.DrawMethod method, string drawTechnique)
{
gd = Statics_Stream.RenderSettings.graphics.GraphicsDevice;
drawMethod = method;
technique = drawTechnique;
}
///
/// Base draw function which calls the required functions depending on the draw method of this object
///
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(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();
}
///
/// Draws a stand alone model with no instancing or triangle stripping.
///
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();
}
}
}
///
/// Draws a stand alone model that uses the triangle-strip content processor for loading.
///
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();
}
}
}
///
/// Draws an instanced model that uses the standard content processor.
///
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();
}
}
}
///
/// Draws an instanced model that uses the triangle-strip content processor.
///
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();
}
}
}
///
/// Draw the animated player model
///
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.");
}
}
///
/// Draw the instanced animated model
///
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.");
}
}
}
}