/// <summary> /// Imports an animation sampler. /// Right now only LINEAR interpolation is supported, thus any other definitions /// are simply ignored and LINEAR is used instead. /// </summary> /// <param name="xmlAnimation">XML animation node</param> /// <param name="xmlSampler">XML sampler node</param> /// <param name="target">Value of sampler's target attribute</param> /// <returns>Animation Sampler</returns> // TODO: Implement import of other interpolation methods than LINEAR static JointAnimationSampler ImportSampler(XmlNode xmlAnimation, XmlNode xmlSampler, string target) { // Input and Output sources Source input = Source.FromInput(xmlSampler.SelectSingleNode("input[@semantic='INPUT']"), xmlAnimation); Source output = Source.FromInput(xmlSampler.SelectSingleNode("input[@semantic='OUTPUT']"), xmlAnimation); // Target (matrix, translation.*, rotation.*, scale.*) target = target.Substring(target.IndexOf('/') + 1); // It is assumed that TIME is used for input var times = input.GetData <float>(); var keyframes = new JointAnimationKeyFrame[times.Count]; if (target == "matrix") { // Baked matrices were used so that there is one animation per joint animating // the whole transform matrix. Now we just need to save these transforms in keyframes var transforms = output.GetData <Matrix>(); Debug.Assert(transforms.Count == times.Count); for (int i = 0; i < times.Count; i++) { keyframes[i] = new JointAnimationKeyFrame(times[i], transforms[i]); } } else if (output.DataType == typeof(float)) { // No baked matrices, i.e. there is an animation for each component of the transform var data = output.GetData <float>(); Debug.Assert(times.Count == data.Count); for (int i = 0; i < times.Count; i++) { keyframes[i] = CreateKeyFrameFromSingle(target, data[i], times[i]); } } else if (output.DataType == typeof(Vector3)) { var data = output.GetData <Vector3>(); Debug.Assert(times.Count == data.Count); for (int i = 0; i < times.Count; i++) { keyframes[i] = CreateKeyFrameFromVector(target, data[i], times[i]); } } return(new JointAnimationSampler(keyframes, AnimationInterpolation.Linear)); }
/// <summary> /// Takes a group of animations which affect the same joint and combines them into /// one animation with one channel. For this all transformations of each individual /// keyframe are combined. Translations are added, Scales multiplied and rotations /// multiplied as well. /// </summary> /// <param name="animations">List of animations</param> /// <returns>Combined animation</returns> static JointAnimation CombineAnimations(IEnumerable <JointAnimation> animations) { if (animations.Count() == 1) { // If there is only one animation there's no need to join return(animations.First()); } // Number of frames that have to be combined int numFrames = animations.First().NumFrames; // All animations must have the same number of frames if (!animations.All(a => a.NumFrames == numFrames)) { throw new NotImplementedException("Animations affecting the same joint must " + "have the same number of keyframes"); } var combinedKeyframes = new JointAnimationKeyFrame[numFrames]; for (int i = 0; i < numFrames; i++) { // Create a combined key frame float time = animations.First().Channels[0].Sampler.Keyframes[i].Time; Vector3 scale = new Vector3(1, 1, 1); Quaternion rotation = Quaternion.Identity; Vector3 translation = new Vector3(0, 0, 0); foreach (var add in animations.Select(anim => anim.Channels[0].Sampler.Keyframes[i])) { if (add.Scale != Vector3.One) { scale *= add.Scale; } if (add.Translation != Vector3.Zero) { translation += add.Translation; } // Single axis rotations are executed in order (as defined in the document) // Note: Not sure if this is correct if (add.Rotation != Quaternion.Identity) { rotation = add.Rotation * rotation; } } var keyframe = new JointAnimationKeyFrame(time, scale, rotation, translation); combinedKeyframes[i] = keyframe; } Joint target = animations.First().Channels[0].Target; var sampler = new JointAnimationSampler(combinedKeyframes, AnimationInterpolation.Linear); JointAnimation animation = new JointAnimation(new JointAnimationChannel[] { new JointAnimationChannel(sampler, target) }); // Names if (animations.Any(a => !String.IsNullOrEmpty(a.Name))) { animation.Name = animations.Where(a => !String.IsNullOrEmpty(a.Name)). Select(a => a.Name).Aggregate((sum, name) => sum + "+" + name); } if (animations.Any(a => !String.IsNullOrEmpty(a.GlobalID))) { animation.GlobalID = animations.Where(a => !String.IsNullOrEmpty(a.GlobalID)). Select(a => a.GlobalID).Aggregate((sum, name) => sum + "\n" + name); } return(animation); }