static bool Test() { #if DEBUG && !MATRIXFRAMES Keyframe kf = new Keyframe(); Matrix mx = Matrix.CreateScale(0.15f, 0.20f, 0.25f) * Matrix.CreateRotationY((float)(Math.PI / 6)) * Matrix.CreateRotationX(0.1f) * Matrix.CreateRotationZ(-0.1f) * Matrix.CreateTranslation(2, 0, 1); Keyframe.CreateFromMatrix(ref mx, kf); Matrix two; kf.ToMatrix(out two); Matrix three = Matrix.Invert(mx) * two; // verify that forward and back is transparent for (int i = 0; i != 16; ++i) { float f = MatrixElement(ref three, i); if ((i % 5) == 0) { System.Diagnostics.Debug.Assert(Math.Abs(f - 1) < 1e-4); } else { System.Diagnostics.Debug.Assert(Math.Abs(f) < 1e-4); } } #endif return(true); }
/// <summary> /// An ultra convenient function that will copy all the keyframes /// to a given Model bone array. Only the bones that are actually /// animated will be affected. /// </summary> /// <param name="model">The model to apply the animation to.</param> public void CopyPoseTo(Model model) { if (appliedTime_ != time_) { CalculateKeyframes(); } int n = model.Bones.Count; if (n > keyframes_.Length) { n = keyframes_.Length; } Matrix mat; unchecked { for (int i = 0; i != n; ++i) { Keyframe kf = keyframes_[i]; if (kf != null) { kf.ToMatrix(out mat); model.Bones[i].Transform = mat; } } } }
/// <summary> /// Decompose the given matrix into a scale/rotation/translation /// keyframe. The method used is not well behaved when the scale /// along one axis is very close to 0, so don't scale down by more /// than about 1/10,000. /// This works for matrices that don't contain shear or off-axis scale. /// </summary> /// <param name="xform">The transform to decompose.</param> /// <param name="ret">The keyframe to decompose into.</param> /// <returns>ret (for convenience)</returns> public static Keyframe CreateFromMatrix(ref Matrix xform, Keyframe ret) { #if DEBUG if (ret == null) { throw new ArgumentNullException("ret"); } #endif #if !MATRIXFRAMES // decompose the matrix into scale, rotation and translaction ret.Scale = new Vector3( (new Vector3(xform.M11, xform.M12, xform.M13)).Length(), (new Vector3(xform.M21, xform.M22, xform.M23)).Length(), (new Vector3(xform.M31, xform.M32, xform.M33)).Length()); ret.Pos = xform.Translation; // I can't extract rotation if one of the axes is zero scale. // That's unfortunate, as someone might want to animation an object // becoming pancaked and spinning. Just don't make it THAT much // of a pancake then. Matrix mat = Matrix.Identity; if (Math.Abs(ret.Scale.X) > 1e-6 && Math.Abs(ret.Scale.Y) > 1e-6 && Math.Abs(ret.Scale.Z) > 1e-6) { Vector3 right = Vector3.Normalize(xform.Right); Vector3 up = Vector3.Normalize(xform.Up - right * Vector3.Dot(xform.Up, right)); Vector3 backward = Vector3.Cross(right, up); if (Vector3.Dot(xform.Backward, backward) < 0) { // matrix is mirrored ret.Scale = ret.Scale * new Vector3(1, 1, -1); } mat.Right = right; mat.Up = up; mat.Backward = backward; ret.Ori = Quaternion.CreateFromRotationMatrix(mat); } ret.ToMatrix(out mat); for (int i = 0; i != 16; ++i) { // Make sure that the matrix that comes out of the keyframe is a good decomposition // of the original matrix. if (Math.Abs(MatrixElement(ref mat, i) - MatrixElement(ref xform, i)) > 1e-2) { #if DEBUG System.Diagnostics.Debugger.Break(); #endif throw new System.ArgumentException("Matrix could not be properly decomposed into TRS keyframe."); } } #else ret.transform_ = xform; #endif return(ret); }
/// <summary> /// Extract the pose as a number of parent-relative matrices. /// Note that this is not typically what you want to use for /// rendering, as you then want object-relative matrices. /// </summary> /// <param name="output">Each bones parent relative matrix will be stored /// here, if the bone is animated. Else that matrix will be untouched.</param> public void CopyPoseTo(Matrix[] output) { if (appliedTime_ != time_) { CalculateKeyframes(); } int n = output.Length; if (n > keyframes_.Length) { n = keyframes_.Length; } unchecked { for (int i = 0; i != n; ++i) { Keyframe kf = keyframes_[i]; if (kf != null) { kf.ToMatrix(out output[i]); } } } }