/// <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); } }
/// <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]; if (currentKeyframe == ClipBone.Keyframes.Count - 1) Keyframe2 = Keyframe1; else Keyframe2 = ClipBone.Keyframes[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); } } } }