/// <summary> /// キーフレームを時間順に並べ替えるための比較用メソッド /// </summary> static int CompareKeyframeTimes(Keyframe a, Keyframe b) { return a.Time.CompareTo(b.Time); }
/// <summary> /// Converts an intermediate format content pipeline AnimationContentDictionary /// object to our runtime AnimationClip format. /// </summary> static Dictionary<string, AnimationClip> ProcessAnimations( AnimationContentDictionary animations, IList<BoneContent> bones, ContentProcessorContext context, ContentIdentity sourceIdentity) { // Build up a table mapping bone names to indices. Dictionary<string, int> boneMap = new Dictionary<string, int>(); for (int i = 0; i < bones.Count; i++) { string boneName = bones[i].Name; if (!string.IsNullOrEmpty(boneName)) boneMap.Add(boneName, i); } // Convert each animation in turn. Dictionary<string, AnimationClip> animationClips; animationClips = new Dictionary<string, AnimationClip>(); foreach (KeyValuePair<string, AnimationContent> animation in animations) { AnimationClip processed = ProcessAnimation(animation.Value, boneMap, animation.Key); animationClips.Add(animation.Key, processed); } // 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)) { context.AddDependency(AnimPath); AnimationDefinition AnimDef = context.BuildAndLoadAsset<XmlImporter, AnimationDefinition>(new ExternalReference<XmlImporter>(AnimPath), null); //breaks up original animation clips into new clips if (animationClips.ContainsKey(AnimDef.originalClipName)) { //graps main clip AnimationClip MainClip = animationClips[AnimDef.originalClipName]; //remove original clip from animations animationClips.Remove(AnimDef.originalClipName); foreach (AnimationDefinition.clipPart Part in AnimDef.ClipParts) { //calculate frame times TimeSpan StartTime = GetTimeSpanForFrame(Part.StartFrame, AnimDef.originalFrameCount, MainClip.Duration.Ticks); TimeSpan EndTime = GetTimeSpanForFrame(Part.EndFrame, AnimDef.originalFrameCount, MainClip.Duration.Ticks); //get keyframes for animation clip thats in start and end time List<Keyframe> Keyframes = new List<Keyframe>(); foreach (Keyframe AnimFrame in MainClip.Keyframes) { if ((AnimFrame.Time >= StartTime) && (AnimFrame.Time <= EndTime)) { Keyframe NewFrame = new Keyframe(AnimFrame.Bone, AnimFrame.Time - StartTime, AnimFrame.Transform); Keyframes.Add(NewFrame); } } //process events /*List<AnimationEvent> Events = new List<AnimationEvent>(); if (Part.Events != null) { foreach (AnimationDefinition.clipPart.Event Event in Part.Events) { TimeSpan EventTime = GetTimeSpanForFrame(Event.Keyframe, AnimDef.originalFrameCount, MainClip.Duration.Ticks); EventTime -= StartTime; AnimationEvent newEvent = new AnimationEvent(); newEvent.EventTime = EventTime; newEvent.EventName = Event.Name; Events.Add(newEvent); } }*/ AnimationClip newClip = new AnimationClip(EndTime - StartTime, Keyframes, Part.ClipName); animationClips[Part.ClipName] = newClip; } } } /* if (animationClips.Count == 0) { throw new InvalidContentException( "Input file does not contain any animations."); }*/ return animationClips; }
private static int CompareKeyframeTimes(Keyframe a, Keyframe b) { TimeSpan time = a.Time; int num = time.CompareTo(b.Time); return num; }
AnimationClip ExtractAnimation(AnimationClip rootAnimation, int startFrame, int endFrame) { TimeSpan startTime = ConvertFrameNumberToTimeSpan(startFrame); TimeSpan endTime = ConvertFrameNumberToTimeSpan(endFrame); List<Keyframe> keyframes = new List<Keyframe>(); foreach (Keyframe keyframe in rootAnimation.Keyframes) { if (keyframe.Time >= startTime && keyframe.Time <= endTime) { Keyframe newKeyframe = new Keyframe(keyframe.Bone, keyframe.Time - startTime, keyframe.Transform); keyframes.Add(newKeyframe); } } return new AnimationClip(endTime - startTime, keyframes); }
/// <summary> /// Helper used by the Update method to refresh the BoneTransforms data. /// </summary> public void UpdateBoneTransforms(TimeSpan time, bool relativeToCurrentTime) { if (currentClipValue == null) { throw new InvalidOperationException( "AnimationPlayer.Update was called before StartClip"); } // Store the previous time TimeSpan lastTime = time; // Update the animation position. if (relativeToCurrentTime) { lastTime = currentTimeValue; time += currentTimeValue; // Check for events CheckEvents(ref time, ref lastTime); // If we reached the end, loop back to the start. bool hasLooped = false; while (time >= currentClipValue.Duration) { hasLooped = true; time -= currentClipValue.Duration; } // If we've looped, reprocess the events if (hasLooped) { CheckEvents(ref time, ref lastTime); } } if ((time < TimeSpan.Zero) || (time >= currentClipValue.Duration)) { throw new ArgumentOutOfRangeException("time"); } // If the position moved backwards, reset the keyframe index. bool HasResetKeyframe = false; if (time < currentTimeValue) { HasResetKeyframe = true; currentKeyframe = 0; skinningDataValue.BindPose.CopyTo(boneTransforms, 0); } currentTimeValue = time; // Read keyframe matrices. IList <Keyframe> keyframes = currentClipValue.Keyframes; while (currentKeyframe < keyframes.Count) { Keyframe keyframe = keyframes[currentKeyframe]; // Stop when we've read up to the current time position. if ((keyframe.Time > currentTimeValue) && (!HasResetKeyframe)) { break; } // Use this keyframe. boneTransforms[keyframe.Bone] = keyframe.Transform; currentKeyframe++; if (HasResetKeyframe) { currentTimeValue = keyframe.Time; HasResetKeyframe = false; } } }
/// <summary> /// Converts an intermediate format content pipeline AnimationContentDictionary /// object to our runtime AnimationClip format. /// </summary> static Dictionary<string, AnimationClip> ProcessAnimations( AnimationContentDictionary animations, IList<BoneContent> bones, ContentProcessorContext context, ContentIdentity sourceIdentity) { // Build up a table mapping bone names to indices. Dictionary<string, int> boneMap = new Dictionary<string, int>(); for (int i = 0; i < bones.Count; i++) { string boneName = bones[i].Name; if (!string.IsNullOrEmpty(boneName)) boneMap.Add(boneName, i); } // Convert each animation in turn. Dictionary<string, AnimationClip> animationClips; animationClips = new Dictionary<string, AnimationClip>(); // We process the original animation first, so we can use their keyframes foreach (KeyValuePair<string, AnimationContent> animation in animations) { AnimationClip processed = ProcessAnimation(animation.Value, boneMap, animation.Key); animationClips.Add(animation.Key, processed); } // 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); // Break up the original animation clips into our new clips // First, we check if the clips contains our clip to break up if (animationClips.ContainsKey(AnimDef.OriginalClipName)) { // Grab the main clip that we are using AnimationClip MainClip = animationClips[AnimDef.OriginalClipName]; // Now remove the original clip from our animations animationClips.Remove(AnimDef.OriginalClipName); // Process each of our new animation clip parts foreach (AnimationDefinition.ClipPart Part in AnimDef.ClipParts) { // Calculate the frame times TimeSpan StartTime = GetTimeSpanForFrame(Part.StartFrame, AnimDef.OriginalFrameCount, MainClip.Duration.Ticks); TimeSpan EndTime = GetTimeSpanForFrame(Part.EndFrame, AnimDef.OriginalFrameCount, MainClip.Duration.Ticks); // Get all the keyframes for the animation clip // that fall within the start and end time List<Keyframe> Keyframes = new List<Keyframe>(); foreach (Keyframe AnimFrame in MainClip.Keyframes) { if ((AnimFrame.Time >= StartTime) && (AnimFrame.Time <= EndTime)) { Keyframe NewFrame = new Keyframe(AnimFrame.Bone, AnimFrame.Time - StartTime, AnimFrame.Transform); Keyframes.Add(NewFrame); } } // Process the events List<AnimationEvent> Events = new List<AnimationEvent>(); if (Part.Events != null) { // Process each event foreach (AnimationDefinition.ClipPart.Event Event in Part.Events) { // Get the event time within the animation TimeSpan EventTime = GetTimeSpanForFrame(Event.Keyframe, AnimDef.OriginalFrameCount, MainClip.Duration.Ticks); // Offset the event time so it is relative to the start of the animation EventTime -= StartTime; // Create the event AnimationEvent NewEvent = new AnimationEvent(); NewEvent.EventTime = EventTime; NewEvent.EventName = Event.Name; Events.Add(NewEvent); } } // Create the clip AnimationClip NewClip = new AnimationClip(EndTime - StartTime, Keyframes, Events, Part.ClipName); animationClips[Part.ClipName] = NewClip; } } } if (animationClips.Count == 0) { throw new InvalidContentException( "Input file does not contain any animations."); } return animationClips; }