Specialised KeyFrame which stores a full transform. The members are public, but any client that assigns to a member instead of using the setter must call Changed()
Inheritance: KeyFrame
Beispiel #1
0
        /// <summary>
        ///     Method to determine if this track has any KeyFrames which are
        ///     doing anything useful - can be used to determine if this track
        ///     can be optimised out.
        /// </summary>
        public override bool HasNonZeroKeyFrames()
        {
            for (int i = 0; i < keyFrameList.Count; i++)
            {
                KeyFrame keyFrame = keyFrameList[i];
                // look for keyframes which have any component which is non-zero
                // Since exporters can be a little inaccurate sometimes we use a
                // tolerance value rather than looking for nothing
                TransformKeyFrame kf    = (TransformKeyFrame)keyFrame;
                Vector3           trans = kf.Translate;
                Vector3           scale = kf.Scale;
                Vector3           axis  = Vector3.Zero;
                float             angle = 0f;
                kf.Rotation.ToAngleAxis(ref angle, ref axis);
                float tolerance = 1e-3f;
                if (trans.Length > tolerance ||
                    (scale - Vector3.UnitScale).Length > tolerance ||
                    !MathUtil.FloatEqual(MathUtil.DegreesToRadians(angle), 0.0f, tolerance))
                {
                    return(true);
                }
            }

            return(false);
        }
Beispiel #2
0
        /// <summary>Used to rebuild the internal interpolation splines for translations, rotations, and scaling.</summary>
        protected void BuildInterpolationSplines()
        {
            // dont calculate on the fly, wait till the end when we do it manually
            positionSpline.AutoCalculate = false;
            rotationSpline.AutoCalculate = false;
            scaleSpline.AutoCalculate    = false;

            positionSpline.Clear();
            rotationSpline.Clear();
            scaleSpline.Clear();

            // add spline control points for each keyframe in the list
            for (int i = 0; i < keyFrameList.Count; i++)
            {
                TransformKeyFrame keyFrame = (TransformKeyFrame)keyFrameList[i];

                positionSpline.AddPoint(keyFrame.Translate);
                rotationSpline.AddPoint(keyFrame.Rotation);
                scaleSpline.AddPoint(keyFrame.Scale);
            }

            // recalculate all spline tangents now
            positionSpline.RecalculateTangents();
            rotationSpline.RecalculateTangents();
            scaleSpline.RecalculateTangents();

            isSplineRebuildNeeded = false;
        }
Beispiel #3
0
        /// <summary>
        ///		Same as the Apply method, but applies to a specified Node instead of it's associated node.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="time"></param>
        /// <param name="weight"></param>
        /// <param name="accumulate"></param>
        public void ApplyToNode(Node node, float time, float weight, bool accumulate, float scale)
        {
            TransformKeyFrame kf = new TransformKeyFrame(null, time);

            this.GetInterpolatedKeyFrame(time, kf);

            if (accumulate)
            {
                // add to existing. Weights are not relative, but treated as absolute multipliers for the animation
                Vector3 translate = kf.Translate * weight * scale;
                node.Translate(translate);

                // interpolate between not rotation and full rotation, to point weight, so 0 = no rotate, and 1 = full rotation
                Quaternion rotate = Quaternion.Slerp(weight, Quaternion.Identity, kf.Rotation);
                node.Rotate(rotate);

                // TODO: not yet sure how to modify scale for cumulative animations
                Vector3 scaleVector = kf.Scale;
                // Not sure how to modify scale for cumulative anims... leave it alone
                //scaleVector = ((Vector3::UNIT_SCALE - kf.getScale()) * weight) + Vector3::UNIT_SCALE;
                if (scale != 1.0f && scaleVector != Vector3.UnitScale)
                {
                    scaleVector = Vector3.UnitScale + (scaleVector - Vector3.UnitScale) * scale;
                }
                node.Scale(scaleVector);
            }
            else
            {
                // apply using weighted transform method
                node.WeightedTransform(weight, ref kf.translate, ref kf.rotation, ref kf.scale, false);
            }
        }
Beispiel #4
0
        /// <summary> Optimise the current track by removing any duplicate keyframes. </summary>
        public override void Optimise()
        {
            // Eliminate duplicate keyframes from 2nd to penultimate keyframe
            // NB only eliminate middle keys from sequences of 5+ identical keyframes
            // since we need to preserve the boundary keys in place, and we need
            // 2 at each end to preserve tangents for spline interpolation
            Vector3      lasttrans       = Vector3.Zero;
            Vector3      lastscale       = Vector3.Zero;
            Quaternion   lastorientation = Quaternion.Zero;
            float        tolerance       = 1e-3f;
            float        quatTolerance   = 1e-3f;
            ushort       k          = 0;
            ushort       dupKfCount = 0;
            List <short> removeList = new List <short>();

            for (int i = 0; i < keyFrameList.Count; i++)
            {
                KeyFrame          keyFrame       = keyFrameList[i];
                TransformKeyFrame kf             = (TransformKeyFrame)keyFrame;
                Vector3           newtrans       = kf.Translate;
                Vector3           newscale       = kf.Scale;
                Quaternion        neworientation = kf.Rotation;
                // Ignore first keyframe; now include the last keyframe as we eliminate
                // only k-2 in a group of 5 to ensure we only eliminate middle keys
                if (i != 0 &&
                    newtrans.DifferenceLessThan(lasttrans, tolerance) &&
                    newscale.DifferenceLessThan(lastscale, tolerance) &&
                    neworientation.Equals(lastorientation, quatTolerance))
                {
                    ++dupKfCount;

                    // 4 indicates this is the 5th duplicate keyframe
                    if (dupKfCount == 4)
                    {
                        // remove the 'middle' keyframe
                        removeList.Add((short)(k - 2));
                        --dupKfCount;
                    }
                }
                else
                {
                    // reset
                    dupKfCount      = 0;
                    lasttrans       = newtrans;
                    lastscale       = newscale;
                    lastorientation = neworientation;
                }
            }

            // Now remove keyframes, in reverse order to avoid index revocation
            for (int i = removeList.Count - 1; i >= 0; i--)
            {
                RemoveKeyFrame(removeList[i]);
            }
        }
 protected XmlElement WriteKeyFrame(TransformKeyFrame keyFrame)
 {
     XmlElement node = document.CreateElement("keyframe");
     XmlAttribute attr;
     attr = document.CreateAttribute("time");
     attr.Value = keyFrame.Time.ToString();
     node.Attributes.Append(attr);
     XmlElement translate = WriteTranslate(keyFrame.Translate);
     node.AppendChild(translate);
     XmlElement rotate = WriteRotate(keyFrame.Rotation);
     node.AppendChild(rotate);
     return node;
 }
Beispiel #6
0
		protected void WriteKeyFrame( BinaryWriter writer, TransformKeyFrame keyFrame )
		{
			long start_offset = writer.Seek( 0, SeekOrigin.Current );
			WriteChunk( writer, SkeletonChunkID.KeyFrame, 0 );

			WriteFloat( writer, keyFrame.Time );
			WriteQuat( writer, keyFrame.Rotation );
			WriteVector3( writer, keyFrame.Translate );
			if ( keyFrame.Scale != Vector3.UnitScale )
				WriteVector3( writer, keyFrame.Scale );

			long end_offset = writer.Seek( 0, SeekOrigin.Current );
			writer.Seek( (int)start_offset, SeekOrigin.Begin );
			WriteChunk( writer, SkeletonChunkID.KeyFrame, (int)( end_offset - start_offset ) );
			writer.Seek( (int)end_offset, SeekOrigin.Begin );
		}
Beispiel #7
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="fileName"></param>
        public void DumpContents(string fileName)
        {
            FileStream   fs     = File.Open(fileName, FileMode.Create);
            StreamWriter writer = new StreamWriter(fs);

            writer.AutoFlush = true;

            writer.WriteLine("-= Debug output of skeleton  {0} =-", this.name);
            writer.WriteLine("");
            writer.WriteLine("== Bones ==");
            writer.WriteLine("Number of bones: {0}", boneList.Count);

            Quaternion q     = new Quaternion();
            float      angle = 0;
            Vector3    axis  = new Vector3();

            // write each bone out
            foreach (Bone bone in boneList.Values)
            {
                writer.WriteLine("-- Bone {0} --", bone.Handle);
                writer.Write("Position: {0}", bone.Position);
                q = bone.Orientation;
                writer.Write("Rotation: {0}", q);
                q.ToAngleAxis(ref angle, ref axis);
                writer.Write(" = {0} radians around axis {1}", angle, axis);
                writer.WriteLine(""); writer.WriteLine("");
            }

            writer.WriteLine("== Animations ==");
            writer.WriteLine("Number of animations: {0}", animationList.Count);

            // animations
            foreach (Animation anim in animationList)
            {
                writer.WriteLine("-- Animation '{0}' (length {1}) --", anim.Name, anim.Length);
                writer.WriteLine("Number of tracks: {0}", anim.NodeTracks.Count);

                // tracks
                foreach (NodeAnimationTrack track in anim.NodeTracks.Values)
                {
                    writer.WriteLine("  -- AnimationTrack {0} --", track.Handle);
                    writer.WriteLine("  Affects bone: {0}", ((Bone)track.TargetNode).Handle);
                    writer.WriteLine("  Number of keyframes: {0}", track.KeyFrames.Count);

                    // key frames
                    int kf = 0;
                    for (ushort i = 0; i < track.KeyFrames.Count; i++)
                    {
                        TransformKeyFrame keyFrame = track.GetNodeKeyFrame(i);
                        writer.WriteLine("    -- KeyFrame {0} --", kf++);
                        writer.Write("    Time index: {0}", keyFrame.Time);
                        writer.WriteLine("    Translation: {0}", keyFrame.Translate);
                        q = keyFrame.Rotation;
                        writer.Write("    Rotation: {0}", q);
                        q.ToAngleAxis(ref angle, ref axis);
                        writer.WriteLine(" = {0} radians around axis {1}", angle, axis);
                    }
                }
            }

            writer.Close();
            fs.Close();
        }
        /// <summary>
        ///		Same as the Apply method, but applies to a specified Node instead of it's associated node.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="time"></param>
        /// <param name="weight"></param>
        /// <param name="accumulate"></param>
        public void ApplyToNode(Node node, float time, float weight, bool accumulate, float scale)
        {
            TransformKeyFrame kf = new TransformKeyFrame(null, time);
            this.GetInterpolatedKeyFrame(time, kf);

            if(accumulate) {
                // add to existing. Weights are not relative, but treated as absolute multipliers for the animation
                Vector3 translate = kf.Translate * weight * scale;
                node.Translate(translate);

                // interpolate between not rotation and full rotation, to point weight, so 0 = no rotate, and 1 = full rotation
                Quaternion rotate = Quaternion.Slerp(weight, Quaternion.Identity, kf.Rotation);
                node.Rotate(rotate);

                // TODO: not yet sure how to modify scale for cumulative animations
                Vector3 scaleVector = kf.Scale;
                // Not sure how to modify scale for cumulative anims... leave it alone
                //scaleVector = ((Vector3::UNIT_SCALE - kf.getScale()) * weight) + Vector3::UNIT_SCALE;
                if (scale != 1.0f && scaleVector != Vector3.UnitScale)
                    scaleVector = Vector3.UnitScale + (scaleVector - Vector3.UnitScale) * scale;
                node.Scale(scaleVector);
            }
            else {
                // apply using weighted transform method
                node.WeightedTransform(weight, ref kf.translate, ref kf.rotation, ref kf.scale, false);
            }
        }
 protected void ReadRotate(XmlNode node, TransformKeyFrame keyFrame)
 {
     float angle = float.Parse(node.Attributes["angle"].Value);
     foreach (XmlNode childNode in node.ChildNodes) {
         switch (childNode.Name) {
             case "axis":
                 Vector3 axis = ReadVector3(childNode);
                 keyFrame.Rotation = Quaternion.FromAngleAxis(angle, axis);
                 break;
             default:
                 DebugMessage(childNode);
                 break;
         }
     }
 }
        public static void CleanupAnimation(Skeleton skel, Animation anim)
        {
            Animation newAnim = skel.CreateAnimation("_replacement", anim.Length);
            Animation tmpAnim = skel.CreateAnimation("_temporary", anim.Length);
            foreach (NodeAnimationTrack track in anim.NodeTracks.Values) {
                Bone bone = skel.GetBone((ushort)track.Handle);
                NodeAnimationTrack newTrack = newAnim.CreateNodeTrack(track.Handle, bone);
                NodeAnimationTrack tmpTrack = tmpAnim.CreateNodeTrack(track.Handle, bone);
                int maxFrame = track.KeyFrames.Count;
                int lastKeyFrame = -1;
                for (int keyFrameIndex = 0; keyFrameIndex < track.KeyFrames.Count; ++keyFrameIndex) {
                    if (anim.InterpolationMode == InterpolationMode.Linear) {
                        // Linear is based on one point before and one after.
                        TransformKeyFrame cur = track.GetTransformKeyFrame(keyFrameIndex);
                        if (keyFrameIndex == 0 ||
                            keyFrameIndex == (track.KeyFrames.Count - 1)) {
                            // Add the key frame if it is the first or last keyframe.
                            lastKeyFrame = keyFrameIndex;
                            DuplicateKeyFrame(newTrack, cur);
                        } else {
                            // Make sure tmpTrack is clean.. we just use it for interpolation
                            tmpTrack.RemoveAllKeyFrames();
                            TransformKeyFrame prior = track.GetTransformKeyFrame(lastKeyFrame);
                            TransformKeyFrame next = track.GetTransformKeyFrame(keyFrameIndex + 1);
                            DuplicateKeyFrame(tmpTrack, prior);
                            DuplicateKeyFrame(tmpTrack, next);
                            // Check to see if removing this last keyframe will throw off
                            // any of the other keyframes that were considered redundant.
                            bool needKeyFrame = false;
                            for (int i = lastKeyFrame + 1; i <= keyFrameIndex; ++i) {
                                TransformKeyFrame orig = track.GetTransformKeyFrame(i);
                                TransformKeyFrame interp = new TransformKeyFrame(tmpTrack, orig.Time);
                                tmpTrack.GetInterpolatedKeyFrame(orig.Time, interp);
                                // Is this interpolated frame useful or redundant?
                                if (!CompareKeyFrames(interp, cur)) {
                                    needKeyFrame = true;
                                    break;
                                }
                            }
                            if (needKeyFrame) {
                                lastKeyFrame = keyFrameIndex;
                                DuplicateKeyFrame(newTrack, cur);
                            }
                        }
                    } else if (anim.InterpolationMode == InterpolationMode.Spline) {
                        // Spline is based on two points before and two after.
                        TransformKeyFrame cur = track.GetTransformKeyFrame(keyFrameIndex);
            #if DISABLED_CODE
                        if (keyFrameIndex == 0 ||
                            keyFrameIndex == 1 ||
                            keyFrameIndex == (track.KeyFrames.Count - 1) ||
                            keyFrameIndex == (track.KeyFrames.Count - 2)) {
                            // Add the key frame if it is the first, second, last or second to last keyframe.
                            DuplicateKeyFrame(newTrack, cur);
                        } else {
                            // Make sure tmpTrack is clean.. we just use it for interpolation
                            tmpTrack.RemoveAllKeyFrames();
                            TransformKeyFrame prior1 = track.GetTransformKeyFrame(keyFrameIndex - 2);
                            TransformKeyFrame prior2 = track.GetTransformKeyFrame(keyFrameIndex - 1);
                            TransformKeyFrame next1 = track.GetTransformKeyFrame(keyFrameIndex + 1);
                            TransformKeyFrame next2 = track.GetTransformKeyFrame(keyFrameIndex + 2);
                            DuplicateKeyFrame(tmpTrack, prior1);
                            DuplicateKeyFrame(tmpTrack, prior2);
                            DuplicateKeyFrame(tmpTrack, next1);
                            DuplicateKeyFrame(tmpTrack, next2);
                            TransformKeyFrame interp = new TransformKeyFrame(tmpTrack, cur.Time);
                            tmpTrack.GetInterpolatedKeyFrame(cur.Time, interp);
                            // Is this interpolated frame useful or redundant?
                            if (!CompareKeyFrames(interp, cur))
                                DuplicateKeyFrame(newTrack, cur);
                        }
            #else
                        DuplicateKeyFrame(newTrack, cur);
            #endif
                    } else {
                        System.Diagnostics.Debug.Assert(false, "Invalid InterpolationMode: " + anim.InterpolationMode);
                    }
                }
            }
            skel.RemoveAnimation(tmpAnim.Name);
            skel.RemoveAnimation(newAnim.Name);
            skel.RemoveAnimation(anim.Name);

            // Recreate the animation with the proper name (awkward)
            anim = skel.CreateAnimation(anim.Name, anim.Length);
            foreach (NodeAnimationTrack track in newAnim.NodeTracks.Values) {
                Bone bone = skel.GetBone((ushort)track.Handle);
                NodeAnimationTrack newTrack = anim.CreateNodeTrack(track.Handle, bone);
                foreach (KeyFrame keyFrame in track.KeyFrames)
                    DuplicateKeyFrame(newTrack, (TransformKeyFrame)keyFrame);
            }
        }
 public static void DuplicateKeyFrame(NodeAnimationTrack track, TransformKeyFrame orig)
 {
     TransformKeyFrame newKeyFrame = (TransformKeyFrame)track.CreateKeyFrame(orig.Time);
     newKeyFrame.Scale = orig.Scale;
     newKeyFrame.Rotation = orig.Rotation;
     newKeyFrame.Translate = orig.Translate;
 }
 public static bool CompareKeyFrames(TransformKeyFrame keyFrame1, TransformKeyFrame keyFrame2)
 {
     Vector3 delta = keyFrame1.Translate - keyFrame2.Translate;
     if (delta.LengthSquared > (TranslateEpsilon * TranslateEpsilon))
         return false;
     Quaternion rot = keyFrame1.Rotation * keyFrame2.Rotation.Inverse();
     Vector3 axis = Vector3.Zero;
     float angle = 0;
     rot.ToAngleAxis(ref angle, ref axis);
     if (Math.Abs(angle) > RotateEpsilon)
         return false;
     delta = keyFrame1.Scale - keyFrame2.Scale;
     if (delta.LengthSquared > (ScaleEpsilon * ScaleEpsilon))
         return false;
     return true;
 }
Beispiel #13
0
        /// <summary>
        ///		Gets a KeyFrame object which contains the interpolated transforms at the time index specified.
        /// </summary>
        /// <remarks>
        ///		The KeyFrame objects held by this class are transformation snapshots at
        ///		discrete points in time. Normally however, you want to interpolate between these
        ///		keyframes to produce smooth movement, and this method allows you to do this easily.
        ///		In animation terminology this is called 'tweening'.
        /// </remarks>
        /// <param name="time">The time (in relation to the whole animation sequence).</param>
        /// <returns>
        ///		A new keyframe object containing the interpolated transforms. Note that the
        ///		position and scaling transforms are linearly interpolated (lerp), whilst the rotation is
        ///		spherically linearly interpolated (slerp) for the most natural result.
        /// </returns>
        public override KeyFrame GetInterpolatedKeyFrame(float time, KeyFrame kf)
        {
            // note: this is an un-attached keyframe
            TransformKeyFrame result = (TransformKeyFrame)kf;

            // Keyframe pointers
            KeyFrame          kBase1, kBase2;
            TransformKeyFrame k1, k2;
            ushort            firstKeyIndex;

            float t = GetKeyFramesAtTime(time, out kBase1, out kBase2, out firstKeyIndex);

            k1 = (TransformKeyFrame)kBase1;
            k2 = (TransformKeyFrame)kBase2;

            if (t == 0.0f)
            {
                // just use k1
                result.Rotation  = k1.Rotation;
                result.Translate = k1.Translate;
                result.Scale     = k1.Scale;
            }
            else
            {
                // interpolate by t
                InterpolationMode         mode = parent.interpolationMode;
                RotationInterpolationMode rim  = parent.rotationInterpolationMode;

                switch (mode)
                {
                case InterpolationMode.Linear: {
                    // linear interoplation
                    // Rotation
                    // Interpolate to nearest rotation if mUseShortestPath set
                    if (rim == RotationInterpolationMode.Linear)
                    {
                        Quaternion.NlerpRef(ref result.rotation, t, ref k1.rotation, ref k2.rotation, useShortestPath);
                    }
                    else     // RotationInterpolationMode.Spherical
                    {
                        result.rotation = Quaternion.Slerp(t, k1.rotation, k2.rotation, useShortestPath);
                    }
                    result.translate.x = k1.translate.x + ((k2.translate.x - k1.translate.x) * t);
                    result.translate.y = k1.translate.y + ((k2.translate.y - k1.translate.y) * t);
                    result.translate.z = k1.translate.z + ((k2.translate.z - k1.translate.z) * t);
                    result.scale.x     = k1.scale.x + ((k2.scale.x - k1.scale.x) * t);
                    result.scale.y     = k1.scale.y + ((k2.scale.y - k1.scale.y) * t);
                    result.scale.z     = k1.scale.z + ((k2.scale.z - k1.scale.z) * t);
                    result.Changed();
                }   break;

                case InterpolationMode.Spline: {
                    // spline interpolation
                    if (isSplineRebuildNeeded)
                    {
                        BuildInterpolationSplines();
                    }

                    result.Rotation  = rotationSpline.Interpolate(firstKeyIndex, t, useShortestPath);
                    result.Translate = positionSpline.Interpolate(firstKeyIndex, t);
                    result.Scale     = scaleSpline.Interpolate(firstKeyIndex, t);
                }   break;
                }
            }

            // return the resulting keyframe
            return(result);
        }