/// <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, ContentIdentity sourceIdentity) { // 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); // Check to see if there's an animation clip definition // Here, we're checking for a file with the _Anims suffix. // So, if your model is named dude.fbx, we'd add dude_Anims.xml in the same folder // and the pipeline will see the file and use it to override the animations in the // original model file. string SourceModelFile = sourceIdentity.SourceFilename; string SourcePath = Path.GetDirectoryName(SourceModelFile); string AnimFilename = Path.GetFileNameWithoutExtension(SourceModelFile); AnimFilename += "_Anims.xml"; string AnimPath = Path.Combine(SourcePath, AnimFilename); if (File.Exists(AnimPath)) { // Add the filename as a dependency, so if it changes, the model is rebuilt context.AddDependency(AnimPath); // Load the animation definition from the XML file AnimationDefinition AnimDef = context.BuildAndLoadAsset<XmlImporter, AnimationDefinition>(new ExternalReference<XmlImporter>(AnimPath), null); if (modelExtra.Clips.Count > 0) //if there are some animations in our model { foreach (AnimationDefinition.ClipPart Part in AnimDef.ClipParts) { // Grab the main clip that we are using and copy to MainClip AnimationClip MainClip = new AnimationClip(); float StartTime = GetTimeSpanForFrame(Part.StartFrame, AnimDef.OriginalFrameCount, modelExtra.Clips[AnimDef.OriginalClipName].Duration); float EndTime = GetTimeSpanForFrame(Part.EndFrame, AnimDef.OriginalFrameCount, modelExtra.Clips[AnimDef.OriginalClipName].Duration); MainClip.Duration = EndTime-StartTime; MainClip.Name = modelExtra.Clips[AnimDef.OriginalClipName].Name; // Process each of our new animation clip parts for (int i = 0; i < modelExtra.Clips[AnimDef.OriginalClipName].Bones.Count; i++) { AnimationClip.Bone clipBone = new AnimationClip.Bone(); clipBone.Name = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Name; LinkedList<AnimationClip.Keyframe> keyframes = new LinkedList<AnimationClip.Keyframe>(); if (modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes.Count != 0) { for (int j = 0; j < modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes.Count; j++) { if ((modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Time >= StartTime) && (modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Time <= EndTime)) { AnimationClip.Keyframe frame = new AnimationClip.Keyframe(); frame.Rotation = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Rotation; frame.Time = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Time - StartTime; frame.Translation = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Translation; keyframes.AddLast(frame); //clipBone.Keyframes.Add(frame); } } } // LinearKeyframeReduction(keyframes); clipBone.Keyframes = keyframes.ToList<AnimationClip.Keyframe>(); MainClip.Bones.Add(clipBone); } modelExtra.Clips.Add(Part.ClipName, MainClip); } } } // 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("Take 001",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 (KeyValuePair<string,AnimationClip> clip in modelExtra.Clips) { for (int b = 0; b < bones.Count; b++) { List<AnimationClip.Keyframe> keyframes = clip.Value.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); } } } }
/// <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(clipName,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); } foreach (AnimationClip.Keyframe keyframe in keyframes) { clip.Bones[boneIndex].Keyframes.Add(keyframe); } } } foreach (NodeContent child in input.Children) { ProcessAnimationsRecursive(child); } }
/// <summary> /// Reset back to time zero. /// </summary> //public void Rewind() //{ // Position = 0; //} /// <summary> /// Update the clip position /// </summary> /// <param name="delta"></param> public void Update(GameTime gameTime) { #region Update current_clip current_Position = current_Position + (float)gameTime.ElapsedGameTime.TotalSeconds; if (looping && current_Position >= current_clip.Duration) current_Position = 0; #endregion /*#region Update current_clip Position = Position + (float)gameTime.ElapsedGameTime.TotalSeconds; if (looping && Position >= Duration) Position = 0; #endregion */ //if not blending, copy current transforms; if (next_clip == null) { #region Copying for (int i = 0; i < current_clip.Bones.Count(); i++) { blendedBones[i] = new AnimationClip.Bone(); blendedBones[i].Name = current_clip.Bones[i].Name; for (int j = 0; j < current_clip.Bones[i].Keyframes.Count(); j++) { blendedBones[i].Keyframes.Add(new AnimationClip.Keyframe()); blendedBones[i].Keyframes[j].Rotation = current_clip.Bones[i].Keyframes[j].Rotation; blendedBones[i].Keyframes[j].Translation = current_clip.Bones[i].Keyframes[j].Translation; blendedBones[i].Keyframes[j].Time = current_clip.Bones[i].Keyframes[j].Time; } } #endregion for (int b = 0; b < blendedBones.Length; b++) { // Create it played_boneInfos[b] = new BoneInfo(blendedBones[b]); // Assign it to a model bone played_boneInfos[b].SetModel(model); } return; } //if we get there, means that we blending WOW! // #region Update next_clip // next_Position = next_Position + (float)gameTime.ElapsedGameTime.TotalSeconds; // if (looping && next_Position >= next_clip.Duration) // next_Position = 0; // #endregion if (current_Position < current_clip.Duration) { if (currentblendTime.TotalSeconds > (float)current_clip.Duration) //we try to catch , where are we in current animation currentblendTime = TimeSpan.Zero; else currentblendTime += gameTime.ElapsedGameTime; } float blendAmount = (float)(currentblendTime.TotalSeconds / totalblendTime.TotalSeconds); // Console.WriteLine(blendAmount); if (blendAmount > 1.0f) { current_clip = next_clip; #region Copying for (int i = 0; i < current_clip.Bones.Count(); i++) { blendedBones[i] = new AnimationClip.Bone(); blendedBones[i].Name = current_clip.Bones[i].Name; for (int j = 0; j < current_clip.Bones[i].Keyframes.Count(); j++) { blendedBones[i].Keyframes.Add(new AnimationClip.Keyframe()); blendedBones[i].Keyframes[j].Rotation = current_clip.Bones[i].Keyframes[j].Rotation; blendedBones[i].Keyframes[j].Translation = current_clip.Bones[i].Keyframes[j].Translation; blendedBones[i].Keyframes[j].Time = current_clip.Bones[i].Keyframes[j].Time; } } #endregion next_clip = null; for (int b = 0; b < blendedBones.Length; b++) { // Create it played_boneInfos[b] = new BoneInfo(blendedBones[b]); // Assign it to a model bone played_boneInfos[b].SetModel(model); } // Console.WriteLine("AFTER Blend Clip"); return; } #region Blending Quaternion currentRotation, nextRotation, blendedRotation; Vector3 currentTranslation, nextTranslation, blendedTranslation; for (int i = 0; i < boneCnt; i++) { blendedBones[i] = new AnimationClip.Bone(); blendedBones[i].Name = current_clip.Bones[i].Name; // Console.WriteLine(i + " Kość: " + blendedBones[i].Name + " Klatek: " + current_clip.Bones[i].Keyframes.Count()); if (i == 0 || i == 1) { } else { int h = 10; if (current_clip.Bones[i].Keyframes.Count > next_clip.Bones[i].Keyframes.Count) h = next_clip.Bones[i].Keyframes.Count; else h = current_clip.Bones[i].Keyframes.Count; for (int j = 0; j < h; j++) { blendedBones[i].Keyframes.Add(new AnimationClip.Keyframe()); currentRotation = current_clip.Bones[i].Keyframes[j].Rotation; currentTranslation = current_clip.Bones[i].Keyframes[j].Translation; nextRotation = next_clip.Bones[i].Keyframes[j].Rotation; nextTranslation = next_clip.Bones[i].Keyframes[j].Translation; //Console.WriteLine("Obecna rotacja: "+currentRotation); //Console.WriteLine("Obecna translacja: " + currentTranslation); //Console.WriteLine("Nastepna rotacja: " + nextRotation); //Console.WriteLine("Nastepna translacja: " + nextTranslation); Quaternion.Slerp(ref currentRotation, ref nextRotation, blendAmount, out blendedRotation); Vector3.Lerp(ref currentTranslation, ref nextTranslation, blendAmount, out blendedTranslation); blendedBones[i].Keyframes[j].Rotation = blendedRotation; blendedBones[i].Keyframes[j].Translation = blendedTranslation; blendedBones[i].Keyframes[j].Time = current_clip.Bones[i].Keyframes[j].Time; // Create it played_boneInfos[i] = new BoneInfo(blendedBones[i]); // Assign it to a model bone played_boneInfos[i].SetModel(model); } } } // Console.WriteLine(current_clip.Bones[0].Keyframes.Count()); // Console.WriteLine(current_clip.Bones[1].Keyframes.Count()); // Console.WriteLine(current_clip.Bones[2].Keyframes.Count()); // Console.WriteLine(next_clip.Bones[0].Keyframes.Count()); // Console.WriteLine(next_clip.Bones[1].Keyframes.Count()); // Console.WriteLine(next_clip.Bones[2].Keyframes.Count()); // System.Threading.Thread.Sleep(100000); // Console.WriteLine(blendAmount); #endregion }