/// <summary> /// Decompose the given matrix into a scale/rotation/translation /// keyframe. /// </summary> /// <param name="xform">The transform to decompose.</param> /// <param name="ret">The keyframe to decompose into.</param> /// <returns>ret (for convenience)</returns> protected Keyframe KeyframeFromMatrix(Matrix xform, Keyframe ret) { return(Keyframe.CreateFromMatrix(ref xform, ret)); }
/// <summary> /// The workhorse of the animation processor. It loops through all /// animations, all tracks, and all keyframes, and converts to the format /// expected by the runtime animation classes. /// </summary> /// <param name="input">The NodeContent to process. Comes from the base ModelProcessor.</param> /// <param name="output">The ModelContent that was produced. You don't typically change this.</param> /// <param name="context">The build context (logger, etc).</param> /// <returns>An allocated AnimationSet with the animations to include.</returns> public virtual AnimationSet BuildAnimationSet(NodeContent input, ref ModelContent output, ContentProcessorContext context) { AnimationSet ret = new AnimationSet(); if (!DoAnimations) { context.Logger.LogImportantMessage("DoAnimation is set to false for {0}; not generating animations.", input.Name); return(ret); } // go from name to index Dictionary <string, ModelBoneContent> nameToIndex = new Dictionary <string, ModelBoneContent>(); foreach (ModelBoneContent mbc in output.Bones) { nameToIndex.Add(GetBoneName(mbc), mbc); } AnimationContentDictionary adict = MergeAnimatedBones(input); if (adict == null || adict.Count == 0) { context.Logger.LogWarning("http://kwxport.sourceforge.net/", input.Identity, "Model processed with AnimationProcessor has no animations."); return(ret); } foreach (AnimationContent ac in adict.Values) { if (!IncludeAnimation(ac)) { context.Logger.LogImportantMessage(String.Format("Not including animation named {0}.", ac.Name)); continue; } context.Logger.LogImportantMessage( "Processing animation {0} duration {1} sample rate {2} reduction tolerance {3}.", ac.Name, ac.Duration, SampleRate, Tolerance); AnimationChannelDictionary acdict = ac.Channels; AnimationTrackDictionary tracks = new AnimationTrackDictionary(); foreach (string name in acdict.Keys) { if (!IncludeTrack(ac, name)) { context.Logger.LogImportantMessage(String.Format("Not including track named {0}.", name)); continue; } int ix = 0; AnimationChannel achan = acdict[name]; int bix = nameToIndex[name].Index; context.Logger.LogMessage("Processing bone {0}:{1}.", name, bix); AnimationTrack at; if (tracks.TryGetValue(bix, out at)) { throw new System.ArgumentException( String.Format("Bone index {0} is used by multiple animations in the same clip (name {1}).", bix, name)); } // Sample at given frame rate from 0 .. Duration List <Keyframe> kfl = new List <Keyframe>(); int nFrames = (int)Math.Floor(ac.Duration.TotalSeconds * SampleRate + 0.5); for (int i = 0; i < nFrames; ++i) { Keyframe k = SampleChannel(achan, i / SampleRate, ref ix); kfl.Add(k); } // Run keyframe elimitation Keyframe[] frames = kfl.ToArray(); int nReduced = 0; if (tolerance_ > 0) { nReduced = ReduceKeyframes(frames, tolerance_); } if (nReduced > 0) { context.Logger.LogMessage("Reduced '{2}' from {0} to {1} frames.", frames.Length, frames.Length - nReduced, name); } // Create an AnimationTrack at = new AnimationTrack(bix, frames); tracks.Add(bix, at); } Animation a = new Animation(ac.Name, tracks, SampleRate); ret.AddAnimation(a); } // build the special "identity" and "bind pose" animations AnimationTrackDictionary atd_id = new AnimationTrackDictionary(); AnimationTrackDictionary atd_bind = new AnimationTrackDictionary(); foreach (KeyValuePair <string, ModelBoneContent> nip in nameToIndex) { Keyframe[] frames_id = new Keyframe[2]; frames_id[0] = new Keyframe(); frames_id[1] = new Keyframe(); AnimationTrack at_id = new AnimationTrack(nip.Value.Index, frames_id); atd_id.Add(nip.Value.Index, at_id); Keyframe[] frames_bind = new Keyframe[2]; Matrix mat = nip.Value.Transform; frames_bind[0] = Keyframe.CreateFromMatrix(mat); frames_bind[1] = new Keyframe(); frames_bind[1].CopyFrom(frames_bind[0]); AnimationTrack at_bind = new AnimationTrack(nip.Value.Index, frames_bind); atd_bind.Add(nip.Value.Index, at_bind); } ret.AddAnimation(new Animation("$id$", atd_id, 1.0f)); ret.AddAnimation(new Animation("$bind$", atd_bind, 1.0f)); return(ret); }