/// <inheritdoc/> public void SetIdentity(ref AvatarExpression identity) { identity.LeftEye = AvatarEye.Neutral; identity.LeftEyebrow = AvatarEyebrow.Neutral; identity.Mouth = AvatarMouth.Neutral; identity.RightEye = AvatarEye.Neutral; identity.RightEyebrow = AvatarEyebrow.Neutral; }
/// <summary> /// Initializes a new instance of the <see cref="AvatarPose"/> class for the given skeleton. /// </summary> /// <param name="skeleton">The skeleton.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="skeleton"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="skeleton"/> is not a valid Xbox LIVE Avatar skeleton. /// </exception> public AvatarPose(Skeleton skeleton) { if (skeleton.NumberOfBones != AvatarRenderer.BoneCount) { throw new ArgumentException("The specified skeleton is not a valid Avatar skeleton.", "skeleton"); } _expressionWrapper = new DelegateAnimatableProperty <AvatarExpression>( () => _expression, // Getter e => _expression = e); // Setter _skeletonPose = SkeletonPose.Create(skeleton); }
/// <summary> /// Updates the expression with the correct keyframes based on the current time. /// </summary> /// <param name="playingForward"> /// If true, the animation is playing forward; otherwise, it is playing backwards /// </param> private void UpdateAvatarExpression(bool playingForward) { // Check to see if we have an expression animation if (ExpressionKeyframes == null || ExpressionKeyframes.Count == 0) { return; } if (playingForward) { while (currentExpressionKeyframe < ExpressionKeyframes.Count) { // Get the current keyframe AvatarExpressionKeyFrame keyframe = ExpressionKeyframes[currentExpressionKeyframe]; // Stop when we've read up to the current time. if (keyframe.Time >= currentPosition) { break; } // Set the current expression avatarExpression = keyframe.Expression; // Move the current keyframe forward. currentExpressionKeyframe++; } } else { while (currentExpressionKeyframe >= 0) { // Get the current keyframe AvatarExpressionKeyFrame keyframe = ExpressionKeyframes[currentExpressionKeyframe]; // Stop when we've read back to the current time. if (keyframe.Time <= currentPosition) { break; } // Set the current expression avatarExpression = keyframe.Expression; // Move the current keyframe backwards. currentExpressionKeyframe--; } } }
/// <summary> /// Converts the input expression animation file into expression animation keyframes /// </summary> private List <AvatarExpressionKeyFrame> ProcessExpressionAnimation() { List <AvatarExpressionKeyFrame> expressionAnimationKeyFrames = new List <AvatarExpressionKeyFrame>(); FileStream fs = File.OpenRead(ExpressionFile); StreamReader sr = new StreamReader(fs); while (!sr.EndOfStream) { string currentLine = sr.ReadLine(); // Skip comment lines if (currentLine.StartsWith("#")) { continue; } string[] Components = currentLine.Split(','); // Check for the correct number of components if (Components.Length != 6) { throw new InvalidContentException("Error processing facial expression file"); } try { TimeSpan time = TimeSpan.FromMilliseconds(Convert.ToDouble(Components[0])); AvatarExpression avatarExpression = new AvatarExpression(); avatarExpression.LeftEye = (AvatarEye)Convert.ToInt32(Components[1]); avatarExpression.LeftEyebrow = (AvatarEyebrow)Convert.ToInt32(Components[2]); avatarExpression.Mouth = (AvatarMouth)Convert.ToInt32(Components[3]); avatarExpression.RightEye = (AvatarEye)Convert.ToInt32(Components[4]); avatarExpression.RightEyebrow = (AvatarEyebrow)Convert.ToInt32(Components[5]); AvatarExpressionKeyFrame expressionKeyframe = new AvatarExpressionKeyFrame(time, avatarExpression); expressionAnimationKeyFrames.Add(expressionKeyframe); } catch (Exception) { throw new InvalidContentException("Error processing facial expression file"); } } // Sort the animation frames expressionAnimationKeyFrames.Sort((frame1, frame2) => frame1.Time.CompareTo(frame2.Time)); return(expressionAnimationKeyFrames); }
/// <inheritdoc/> public void Interpolate(ref AvatarExpression source, ref AvatarExpression target, float parameter, ref AvatarExpression result) { result = source; }
/// <inheritdoc/> /// <exception cref="NotSupportedException"><see cref="AvatarExpression"/>s cannot be multiplied.</exception> public void Multiply(ref AvatarExpression value, int factor, ref AvatarExpression result) { throw new NotSupportedException("AvatarExpressions cannot be multiplied."); }
/// <inheritdoc/> /// <exception cref="NotSupportedException"><see cref="AvatarExpression"/>s cannot be added.</exception> public void Add(ref AvatarExpression value0, ref AvatarExpression value1, ref AvatarExpression result) { throw new NotSupportedException("AvatarExpressions cannot be added."); }
/// <inheritdoc/> /// <exception cref="NotSupportedException"> /// <see cref="AvatarExpression"/> does not have an inverse. /// </exception> public void Invert(ref AvatarExpression value, ref AvatarExpression inverse) { throw new NotSupportedException("AvatarExpression animations cannot be looped using the loop behaviors CycleOffset. Use Cycle instead."); }
/// <inheritdoc/> public void Set(ref AvatarExpression value, IAnimatableProperty <AvatarExpression> property) { property.AnimationValue = value; }
/// <summary> /// Constructs a new AvatarExpressionKeyFrame object. /// </summary> public AvatarExpressionKeyFrame(TimeSpan time, AvatarExpression expression) { Time = time; Expression = expression; }
/// <inheritdoc/> public void Recycle(ref AvatarExpression value) { }
/// <inheritdoc/> public void Create(ref AvatarExpression reference, out AvatarExpression value) { value = new AvatarExpression(); }
/// <inheritdoc/> public void EndBlend(ref AvatarExpression value) { }
//-------------------------------------------------------------- #region Avatar Expression Animation //-------------------------------------------------------------- /// <summary> /// Converts the input expression animation file into expression animation keyframes. /// </summary> private AvatarExpressionKeyFrameAnimation ProcessExpressionAnimation(NodeContent input, ContentProcessorContext context) { if (string.IsNullOrEmpty(ExpressionFile)) { return(null); } // Create a AvatarExpression key frame animation that will animate the Expression // property of an AvatarPose. var animation = new AvatarExpressionKeyFrameAnimation { TargetProperty = "Expression", }; // Let the content pipeline know that we depend on this file and we need to rebuild the // content if the file is modified. string sourcePath = Path.GetDirectoryName(input.Identity.SourceFilename); string filePath = Path.GetFullPath(Path.Combine(sourcePath, ExpressionFile)); context.AddDependency(filePath); FileStream fs = File.OpenRead(filePath); StreamReader sr = new StreamReader(fs); while (!sr.EndOfStream) { string currentLine = sr.ReadLine(); // Skip comment lines if (currentLine.StartsWith("#")) { continue; } string[] Components = currentLine.Split(','); // Check for the correct number of components if (Components.Length != 6) { throw new InvalidContentException("Error processing facial expression file", input.Identity); } try { TimeSpan time = TimeSpan.FromMilliseconds(Convert.ToDouble(Components[0])); AvatarExpression avatarExpression = new AvatarExpression(); avatarExpression.LeftEye = (AvatarEye)Convert.ToInt32(Components[1]); avatarExpression.LeftEyebrow = (AvatarEyebrow)Convert.ToInt32(Components[2]); avatarExpression.Mouth = (AvatarMouth)Convert.ToInt32(Components[3]); avatarExpression.RightEye = (AvatarEye)Convert.ToInt32(Components[4]); avatarExpression.RightEyebrow = (AvatarEyebrow)Convert.ToInt32(Components[5]); // Add key frame. var keyFrame = new KeyFrame <AvatarExpression>(time, avatarExpression); animation.KeyFrames.Add(keyFrame); } catch (Exception) { throw new InvalidContentException("Error processing facial expression file", input.Identity); } } if (animation.KeyFrames.Count == 0) { return(null); } // Sort the animation frames animation.KeyFrames.Sort(); return(animation); }
private ITimeline BakeAnimation(AvatarAnimation avatarAnimation) { // Create an AvatarExpression key frame animation that will be applied to the Expression // property of an AvatarPose. AvatarExpressionKeyFrameAnimation expressionAnimation = new AvatarExpressionKeyFrameAnimation { TargetProperty = "Expression" }; // Create a SkeletonPose key frame animation that will be applied to the SkeletonPose // property of an AvatarPose. SkeletonKeyFrameAnimation skeletonKeyFrameAnimation = new SkeletonKeyFrameAnimation { TargetProperty = "SkeletonPose" }; // In the next loop, we sample the original animation with 30 Hz and store the key frames. int numberOfKeyFrames = 0; AvatarExpression previousExpression = new AvatarExpression(); TimeSpan time = TimeSpan.Zero; TimeSpan length = avatarAnimation.Length; TimeSpan step = new TimeSpan(333333); // 1/30 seconds; while (true) { // Advance time in AvatarAnimation. avatarAnimation.CurrentPosition = time; // Add expression key frame if this is the first key frame or if the key frame is // different from the last key frame. AvatarExpression expression = avatarAnimation.Expression; if (time == TimeSpan.Zero || !expression.Equals(previousExpression)) { expressionAnimation.KeyFrames.Add(new KeyFrame <AvatarExpression>(time, expression)); } previousExpression = expression; // Convert bone transforms to SrtTransforms and add key frames to the SkeletonPose // animation. for (int i = 0; i < avatarAnimation.BoneTransforms.Count; i++) { SrtTransform boneTransform = SrtTransform.FromMatrix(avatarAnimation.BoneTransforms[i]); skeletonKeyFrameAnimation.AddKeyFrame(i, time, boneTransform); numberOfKeyFrames++; } // Abort if we have arrived at the end time. if (time == length) { break; } // Increase time. We check that we do not step over the end time. if (time + step > length) { time = length; } else { time += step; } } // Compress animation to save memory. float numberOfRemovedKeyFrames = skeletonKeyFrameAnimation.Compress(0.1f, 0.1f, 0.001f); Debug.WriteLine("Compression removed " + numberOfRemovedKeyFrames * 100 + "% of the key frames."); // Finalize the skeleton key frame animation. This optimizes the internal data structures. skeletonKeyFrameAnimation.Freeze(); return(new TimelineGroup { expressionAnimation, skeletonKeyFrameAnimation, }); }
/// <inheritdoc/> /// <exception cref="NotSupportedException"><see cref="AvatarExpression"/>s cannot be blended.</exception> public void BeginBlend(ref AvatarExpression value) { throw new NotSupportedException("AvatarExpressions cannot be blended."); }
/// <inheritdoc/> /// <exception cref="NotSupportedException"><see cref="AvatarExpression"/>s cannot be blended.</exception> public void BlendNext(ref AvatarExpression value, ref AvatarExpression nextValue, float normalizedWeight) { throw new NotSupportedException("AvatarExpressions cannot be blended."); }
/// <inheritdoc/> public void Copy(ref AvatarExpression source, ref AvatarExpression target) { target = source; }