#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 } }