static AnimationClip ReadClip(BinaryReader reader)
        {
            AnimationClip clip = new AnimationClip();

            clip.Name     = reader.ReadString();
            clip.Duration = reader.ReadDouble();

            int bonesCount = reader.ReadInt32();

            while (bonesCount-- > 0)
            {
                AnimationClip.Bone bone = new AnimationClip.Bone();
                bone.Name = reader.ReadString();

                int keyframesCount = reader.ReadInt32();
                while (keyframesCount-- > 0)
                {
                    AnimationClip.Keyframe keyframe = new AnimationClip.Keyframe();
                    keyframe.Time        = reader.ReadDouble();
                    keyframe.Rotation    = ImportQuaternion(reader);
                    keyframe.Translation = ImportVector3(reader);
                    bone.Keyframes.Add(keyframe);
                }

                clip.Bones.Add(bone);
            }

            return(clip);
        }
        /// <summary>
        /// This function filters out keyframes that can be approximated well with
        /// linear interpolation.
        /// </summary>
        /// <param name="keyframes"></param>
        private void LinearKeyframeReduction(LinkedList <AnimationClip.Keyframe> keyframes)
        {
            if (keyframes.Count < 3)
            {
                return;
            }

            for (LinkedListNode <AnimationClip.Keyframe> node = keyframes.First.Next; ;)
            {
                LinkedListNode <AnimationClip.Keyframe> next = node.Next;
                if (next == null)
                {
                    break;
                }

                // Determine nodes before and after the current node.
                AnimationClip.Keyframe a = node.Previous.Value;
                AnimationClip.Keyframe b = node.Value;
                AnimationClip.Keyframe c = next.Value;

                float t = (float)((node.Value.Time - node.Previous.Value.Time) /
                                  (next.Value.Time - node.Previous.Value.Time));

                Vector3    translation = Vector3.Lerp(a.Translation, c.Translation, t);
                Quaternion rotation    = Quaternion.Slerp(a.Rotation, c.Rotation, t);

                if ((translation - b.Translation).LengthSquared() < TinyLength &&
                    Quaternion.Dot(rotation, b.Rotation) > TinyCosAngle)
                {
                    keyframes.Remove(node);
                }

                node = next;
            }
        }
Exemple #3
0
 /// <summary>
 /// Set the keyframes to a valid value relative to
 /// the current keyframe
 /// </summary>
 void SetKeyframes()
 {
     if (ClipBone == null)
     {
         return;
     }
     if (ClipBone.Keyframes.Count > 0)
     {
         Keyframe1 = ClipBone.Keyframes[m_currentKeyframe];
         if (m_currentKeyframe == ClipBone.Keyframes.Count - 1)
         {
             Keyframe2 = Keyframe1;
         }
         else
         {
             Keyframe2 = ClipBone.Keyframes[m_currentKeyframe + 1];
         }
     }
     else
     {
         // If there are no keyframes, set both to null
         Keyframe1 = null;
         Keyframe2 = null;
     }
 }
        /// <summary>
        /// Entry point for animation processing.
        /// </summary>
        /// <param name="model"></param>
        /// <param name="input"></param>
        /// <param name="context"></param>
        private void ProcessAnimations(ModelContent model, NodeContent input, ContentProcessorContext context)
        {
            // First build a lookup table so we can determine the
            // index into the list of bones from a bone name.
            for (int i = 0; i < model.Bones.Count; i++)
            {
                bones[model.Bones[i].Name] = i;
            }

            // For saving the bone transforms
            boneTransforms = new Matrix[model.Bones.Count];

            //
            // Collect up all of the animation data
            //

            ProcessAnimationsRecursive(input);

            // Ensure there is always a clip, even if none is included in the FBX
            // That way we can create poses using FBX files as one-frame
            // animation clips
            if (modelExtra.Clips.Count == 0)
            {
                AnimationClip clip = new AnimationClip();
                modelExtra.Clips.Add(clip);

                string clipName = "Take 001";

                // Retain by name
                clips[clipName] = clip;

                clip.Name = clipName;
                foreach (ModelBoneContent bone in model.Bones)
                {
                    AnimationClip.Bone clipBone = new AnimationClip.Bone();
                    clipBone.Name = bone.Name;

                    clip.Bones.Add(clipBone);
                }
            }

            // Ensure all animations have a first key frame for every bone
            foreach (AnimationClip clip in modelExtra.Clips)
            {
                for (int b = 0; b < bones.Count; b++)
                {
                    List <AnimationClip.Keyframe> keyframes = clip.Bones[b].Keyframes;
                    if (keyframes.Count == 0 || keyframes[0].Time > 0)
                    {
                        AnimationClip.Keyframe keyframe = new AnimationClip.Keyframe();
                        keyframe.Time      = 0;
                        keyframe.Transform = boneTransforms[b];
                        keyframes.Insert(0, keyframe);
                    }
                }
            }
        }
Exemple #5
0
 /// <summary>
 /// Set the keyframes to a valid value relative to
 /// the current keyframe
 /// </summary>
 private void SetKeyframes()
 {
     if (ClipBone.Keyframes.Count > 0)
     {
         Keyframe1 = ClipBone.Keyframes[currentKeyframe];
         Keyframe2 = currentKeyframe == ClipBone.Keyframes.Count - 1 ? Keyframe1 : ClipBone.Keyframes[currentKeyframe + 1];
     }
     else
     {
         // If there are no keyframes, set both to null
         Keyframe1 = null;
         Keyframe2 = null;
     }
 }
        /// <summary>
        /// Recursive function that processes the entire scene graph, collecting up
        /// all of the animation data.
        /// </summary>
        private void ProcessAnimationsRecursive(NodeContent input)
        {
            // Look up the bone for this input channel
            int inputBoneIndex;

            if (bones.TryGetValue(input.Name, out inputBoneIndex))
            {
                // Save the transform
                boneTransforms[inputBoneIndex] = input.Transform;
            }


            foreach (KeyValuePair <string, AnimationContent> animation in input.Animations)
            {
                // Do we have this animation before?
                AnimationClip clip;
                string        clipName = animation.Key;

                if (!clips.TryGetValue(clipName, out clip))
                {
                    // Never seen before clip
                    clip = new AnimationClip();
                    modelExtra.Clips.Add(clip);

                    // Retain by name
                    clips[clipName] = clip;

                    clip.Name = clipName;
                    foreach (ModelBoneContent bone in model.Bones)
                    {
                        AnimationClip.Bone clipBone = new AnimationClip.Bone();
                        clipBone.Name = bone.Name;

                        clip.Bones.Add(clipBone);
                    }
                }

                // Ensure the duration is always set
                if (animation.Value.Duration.TotalSeconds > clip.Duration)
                {
                    clip.Duration = animation.Value.Duration.TotalSeconds;
                }

                //
                // For each channel, determine the bone and then process all of the
                // keyframes for that bone.
                //

                foreach (KeyValuePair <string, AnimationChannel> channel in animation.Value.Channels)
                {
                    // What is the bone index?
                    int boneIndex;
                    if (!bones.TryGetValue(channel.Key, out boneIndex))
                    {
                        continue;           // Ignore if not a named bone
                    }
                    // An animation is useless if it is for a bone not assigned to any meshes at all
                    if (UselessAnimationTest(boneIndex))
                    {
                        continue;
                    }

                    // I'm collecting up in a linked list so we can process the data
                    // and remove redundant keyframes
                    LinkedList <AnimationClip.Keyframe> keyframes = new LinkedList <AnimationClip.Keyframe>();
                    foreach (AnimationKeyframe keyframe in channel.Value)
                    {
                        Matrix transform = keyframe.Transform;      // Keyframe transformation

                        AnimationClip.Keyframe newKeyframe = new AnimationClip.Keyframe();
                        newKeyframe.Time      = keyframe.Time.TotalSeconds;
                        newKeyframe.Transform = transform;

                        keyframes.AddLast(newKeyframe);
                    }

                    LinearKeyframeReduction(keyframes);
                    foreach (AnimationClip.Keyframe keyframe in keyframes)
                    {
                        clip.Bones[boneIndex].Keyframes.Add(keyframe);
                    }
                }
            }

            foreach (NodeContent child in input.Children)
            {
                ProcessAnimationsRecursive(child);
            }
        }
        static AnimationClip ReadClip(BinaryReader reader)
        {
            AnimationClip clip = new AnimationClip();

            clip.Name     = reader.ReadString();
            clip.Duration = reader.ReadDouble();

            int bonesCount = reader.ReadInt32();

            while (bonesCount-- > 0)
            {
                AnimationClip.Bone bone = new AnimationClip.Bone();
                bone.Name = reader.ReadString();

                int keyframesCount = reader.ReadInt32();
                while (keyframesCount-- > 0)
                {
                    AnimationClip.Keyframe keyframe = new AnimationClip.Keyframe();
                    keyframe.Time        = reader.ReadDouble();
                    keyframe.Rotation    = ImportQuaternion(reader);
                    keyframe.Translation = ImportVector3(reader);
                    bone.Keyframes.Add(keyframe);
                }

                clip.Bones.Add(bone);

                int originalCount = bone.Keyframes.Count;
                int newCount      = 0;
                if (originalCount > 3)
                {
                    if (USE_LINEAR_KEYFRAME_REDUCTION)
                    {
                        LinkedList <AnimationClip.Keyframe> linkedList = new LinkedList <AnimationClip.Keyframe>();
                        foreach (var kf in bone.Keyframes)
                        {
                            linkedList.AddLast(kf);
                        }
                        //LinearKeyframeReduction(linkedList, 0.000001f, 0.985f);
                        //PercentageKeyframeReduction(linkedList, 0.9f);
                        LinearKeyframeReduction(linkedList, TinyLength, TinyCosAngle);
                        bone.Keyframes.Clear();
                        bone.Keyframes.AddArray(linkedList.ToArray());
                        newCount = bone.Keyframes.Count;
                    }
                    if (LINEAR_KEYFRAME_REDUCTION_STATS)
                    {
                        ReductionInfo ri = new ReductionInfo()
                        {
                            BoneName      = bone.Name,
                            OriginalKeys  = originalCount,
                            OptimizedKeys = newCount
                        };

                        List <ReductionInfo> riList;
                        if (!ReductionStats.TryGetValue(m_debugAssetName, out riList))
                        {
                            riList = new List <ReductionInfo>();
                            ReductionStats.Add(m_debugAssetName, riList);
                        }

                        riList.Add(ri);
                    }
                }

                CalculateKeyframeDeltas(bone.Keyframes);
            }

            return(clip);
        }