/* * BonePose.cs * Copyright (c) 2006 David Astle * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Collections.ObjectModel; namespace Xclna.Xna.Animation { /// /// A collection of BonePose objects that represent the bone transforms of a model /// as affected by animations. /// public class BonePoseCollection : System.Collections.ObjectModel.ReadOnlyCollection { // A dictionary for quick access to bone poses based on bone name private Dictionary boneDict = new Dictionary(); // This class should not be externally instantiated internal BonePoseCollection(IList anims) : base(anims) { for (int i = 0; i < anims.Count; i++) { string boneName = anims[i].Name; if (boneName != null && boneName != "" && !boneDict.ContainsKey(boneName)) { boneDict.Add(boneName, anims[i]); } } } // Creates a set of bonepose objects from a skeleton internal static BonePoseCollection FromModelBoneCollection( ModelBoneCollection bones) { BonePose[] anims = new BonePose[bones.Count]; for (int i = 0; i < bones.Count; i++) { if (bones[i].Parent==null) { BonePose ba = new BonePose( bones[i], bones, anims); } } return new BonePoseCollection(anims); } /// /// Computes the absolute transforms for the collection and copies /// the values. /// /// The array into which the transforms will be /// copied. public void CopyAbsoluteTransformsTo(Matrix[] transforms) { for (int i = 0; i < transforms.Length; i++) { if (i > 0) // not root { // This works because the skeleton is always flattened; // the parent index is always lower than the child index. Matrix curTransform = this[i].GetCurrentTransform(); Matrix parentTransform = transforms[this[i].Parent.Index]; Vector3 currentTranslation = curTransform.Translation; Matrix parentRotation = Matrix.CreateFromQuaternion( Quaternion.CreateFromRotationMatrix(parentTransform)); Matrix currentRotation = Matrix.CreateFromQuaternion( Quaternion.CreateFromRotationMatrix(curTransform)); currentTranslation = Vector3.Transform(currentTranslation, parentRotation); currentTranslation += parentTransform.Translation; currentTranslation = parentTransform.Translation + curTransform.Translation; transforms[i] = currentRotation * parentRotation; transforms[i] = curTransform * parentTransform; } else { transforms[i] = this[i].GetCurrentTransform(); } } } /// /// Gets a BonePose object. /// /// The name of the bone for which the BonePose /// will be returned. /// The BonePose associated with the bone name. public BonePose this[string boneName] { get { return boneDict[boneName]; } } } /// /// Represents the current pose of a model bone. /// public class BonePose { // Used when no animation is set private Matrix defaultMatrix; // Buffers for interpolation when blending private static Matrix returnMatrix, blendMatrix, currentMatrixBuffer; private int index; // The bone name private string name; private BonePose parent = null; private IAnimationController currentAnimation = null; private IAnimationController currentBlendAnimation = null; // THe amount to interpolate between the current animation and // the current blend animation private float blendFactor = 0; private BonePoseCollection children; // True if the current animation contains a track for this bone private bool doesAnimContainChannel = false; // True if the current blend animation contains a track for this bone private bool doesBlendContainChannel = false; // Internal creation internal BonePose(ModelBone bone, ModelBoneCollection bones, BonePose[] anims) { // Set the values according to the bone index = bone.Index; name = bone.Name; defaultMatrix = bone.Transform; if (bone.Parent != null) parent = anims[bone.Parent.Index]; anims[index] = this; // Recurse on children List childList = new List(); foreach (ModelBone child in bone.Children) { BonePose newChild = new BonePose( bones[child.Index], bones, anims); childList.Add(newChild); } children = new BonePoseCollection(childList); } /// /// Gets the immediate children of the current bone. /// public BonePoseCollection Children { get { return children; } } // Finds the hierarchy for which this bone is the root private void FindHierarchy(List poses) { poses.Add(this); foreach (BonePose child in children) { child.FindHierarchy(poses); } } /// /// Finds a collection of bones that represents the tree of BonePoses with /// the current BonePose as the root. /// public BonePoseCollection GetHierarchy() { List poses = new List(); FindHierarchy(poses); return new BonePoseCollection(poses); } /// /// Gets the bone's parent. /// public BonePose Parent { get { return parent; } } /// /// Gets the index of the bone. /// public int Index { get { return index; } } /// /// Gets the name of the bone. /// public string Name { get { return name; } } /// /// Gets or sets the current animation that affects this bone. If null, /// then DefaultTransform will be used for this bone's transform. /// public IAnimationController CurrentController { get { return currentAnimation; } set { // Don't do anything if the animation hasn't changed if (currentAnimation != value) { if (value != null) { if (currentAnimation != null) currentAnimation.AnimationTracksChanged -= current_AnimationTracksChanged; if (name != null) { // Update info on whether or not the current anim // contains a track for this bone doesAnimContainChannel = value.ContainsAnimationTrack(this); value.AnimationTracksChanged += new EventHandler(current_AnimationTracksChanged); } } else // A null animation; use defaulttransform doesAnimContainChannel = false; currentAnimation = value; } } } void current_AnimationTracksChanged(object sender, EventArgs e) { doesAnimContainChannel = this.currentAnimation.ContainsAnimationTrack(this); } /// /// Gets or sets the blend animation that affects this bone. If the value /// is null, then no blending will occur. /// public IAnimationController CurrentBlendController { get { return currentBlendAnimation; } set { // Don't do anything if the animation hasn't changed if (currentBlendAnimation != value) { if (value != null) { if (currentBlendAnimation != null) currentBlendAnimation.AnimationTracksChanged -= blend_AnimationTracksChanged; if (name != null) { // Update info on whether or not the current anim // contains a track for this bone doesBlendContainChannel = value.ContainsAnimationTrack(this); value.AnimationTracksChanged += new EventHandler(blend_AnimationTracksChanged); } } else doesBlendContainChannel = false; currentBlendAnimation = value; } } } void blend_AnimationTracksChanged(object sender, EventArgs e) { doesBlendContainChannel = this.currentBlendAnimation.ContainsAnimationTrack(this); } /// /// Gets or sets the amount to interpolate between the current animation and /// the current blend animation, if the current blend animation is not null /// public float BlendFactor { get { return blendFactor; } set { blendFactor = value; } } /// /// Represents the matrix used by the BonePose when it is not affected by /// an animation or when the animation does not contain a track for the bone. /// public Matrix DefaultTransform { get { return defaultMatrix; } set { defaultMatrix = value; } } /// /// Calculates the current transform, based on the animations, for the bone /// represented by the BonePose object. /// public Matrix GetCurrentTransform() { // If the bone is not currently affected by an animation if (currentAnimation == null || !doesAnimContainChannel) { // If the bone is affected by a blend animation, // blend the defaultTransform with the blend animation if (currentBlendAnimation != null && doesBlendContainChannel) { blendMatrix = currentBlendAnimation.GetCurrentBoneTransform(this); Util.SlerpMatrix( ref defaultMatrix, ref blendMatrix, BlendFactor, out returnMatrix); } // else return the default transform else return defaultMatrix; } // The bone is affected by an animation else { // Find the current transform in the animation for the bone currentMatrixBuffer = currentAnimation.GetCurrentBoneTransform(this); // If the bone is affected by a blend animation, blend the // current animation transform with the current blend animation // transform if (currentBlendAnimation != null && doesBlendContainChannel) { blendMatrix = currentBlendAnimation.GetCurrentBoneTransform(this); Util.SlerpMatrix( ref currentMatrixBuffer, ref blendMatrix, BlendFactor, out returnMatrix); } // Else just return the current animation transform else return currentMatrixBuffer; } return returnMatrix; } } }