#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2016 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
#endregion
namespace Microsoft.Xna.Framework.Graphics
{
///
/// A basic 3D model with per mesh parent bones.
///
public class Model
{
#region Public Properties
///
/// A collection of objects which describe how each mesh in the
/// mesh collection for this model relates to its parent mesh.
///
public ModelBoneCollection Bones
{
get;
private set;
}
///
/// A collection of objects which compose the model. Each
/// in a model may be moved independently and may be composed of multiple materials
/// identified as objects.
///
public ModelMeshCollection Meshes
{
get;
private set;
}
///
/// Root bone for this model.
///
public ModelBone Root
{
get;
internal set;
}
///
/// Custom attached object.
///
/// Skinning data is example of attached object for model.
///
///
public object Tag
{
get;
set;
}
#endregion
#region Private Static Variables
private static Matrix[] sharedDrawBoneMatrices;
#endregion
#region Internal Constructor
///
/// Constructs a model.
///
/// A valid reference to .
/// The collection of bones.
/// The collection of meshes.
internal Model(GraphicsDevice graphicsDevice, List bones, List meshes)
{
Bones = new ModelBoneCollection(bones);
Meshes = new ModelMeshCollection(meshes);
}
#endregion
#region Public Methods
///
/// Draws the model meshes.
///
/// The world transform.
/// The view transform.
/// The projection transform.
public void Draw(Matrix world, Matrix view, Matrix projection)
{
int boneCount = Bones.Count;
if (sharedDrawBoneMatrices == null ||
sharedDrawBoneMatrices.Length < boneCount)
{
sharedDrawBoneMatrices = new Matrix[boneCount];
}
// Look up combined bone matrices for the entire model.
CopyAbsoluteBoneTransformsTo(sharedDrawBoneMatrices);
// Draw the model.
foreach (ModelMesh mesh in Meshes)
{
foreach (Effect effect in mesh.Effects)
{
IEffectMatrices effectMatricies = effect as IEffectMatrices;
if (effectMatricies == null)
{
throw new InvalidOperationException();
}
effectMatricies.World = sharedDrawBoneMatrices[mesh.ParentBone.Index] * world;
effectMatricies.View = view;
effectMatricies.Projection = projection;
}
mesh.Draw();
}
}
///
/// Copies bone transforms relative to all parent bones of the each bone from this model to a given array.
///
/// The array receiving the transformed bones.
public void CopyAbsoluteBoneTransformsTo(Matrix[] destinationBoneTransforms)
{
if (destinationBoneTransforms == null)
{
throw new ArgumentNullException("destinationBoneTransforms");
}
if (destinationBoneTransforms.Length < Bones.Count)
{
throw new ArgumentOutOfRangeException("destinationBoneTransforms");
}
int count = Bones.Count;
for (int index1 = 0; index1 < count; index1 += 1)
{
ModelBone modelBone = Bones[index1];
if (modelBone.Parent == null)
{
destinationBoneTransforms[index1] = modelBone.Transform;
}
else
{
int index2 = modelBone.Parent.Index;
Matrix modelBoneTransform = modelBone.Transform;
Matrix.Multiply(
ref modelBoneTransform,
ref destinationBoneTransforms[index2],
out destinationBoneTransforms[index1]
);
}
}
}
///
/// Copies bone transforms relative to bone from a given array to this model.
///
/// The array of prepared bone transform data.
public void CopyBoneTransformsFrom(Matrix[] sourceBoneTransforms)
{
if (sourceBoneTransforms == null)
{
throw new ArgumentNullException("sourceBoneTransforms");
}
if (sourceBoneTransforms.Length < Bones.Count)
{
throw new ArgumentOutOfRangeException("sourceBoneTransforms");
}
for (int i = 0; i < sourceBoneTransforms.Length; i += 1)
{
Bones[i].Transform = sourceBoneTransforms[i];
}
}
///
/// Copies bone transforms relative to bone from this model to a given array.
///
/// The array receiving the transformed bones.
public void CopyBoneTransformsTo(Matrix[] destinationBoneTransforms)
{
if (destinationBoneTransforms == null)
{
throw new ArgumentNullException("destinationBoneTransforms");
}
if (destinationBoneTransforms.Length < Bones.Count)
{
throw new ArgumentOutOfRangeException("destinationBoneTransforms");
}
for (int i = 0; i < destinationBoneTransforms.Length; i += 1)
{
destinationBoneTransforms[i] = Bones[i].Transform;
}
}
#endregion
#region Internal Methods
internal void BuildHierarchy()
{
Matrix globalScale = Matrix.CreateScale(0.01f);
foreach (ModelBone node in Root.Children)
{
BuildHierarchy(node, Root.Transform * globalScale, 0);
}
}
#endregion
#region Private Methods
private void BuildHierarchy(ModelBone node, Matrix parentTransform, int level)
{
node.ModelTransform = node.Transform * parentTransform;
foreach (ModelBone child in node.Children)
{
BuildHierarchy(child, node.ModelTransform, level + 1);
}
}
#endregion
}
}