protected override void GetValueCore(TimeSpan time, ref SkeletonPose defaultSource, ref SkeletonPose defaultTarget, ref SkeletonPose result)
        {
            // Update time of the AvatarAnimation.
            _avatarAnimation.CurrentPosition = time;

            // Get bone transforms and convert to type SrtTransform.
            for (int i = 0; i < AvatarRenderer.BoneCount; i++)
            {
                result.SetBoneTransform(i, SrtTransform.FromMatrix(_avatarAnimation.BoneTransforms[i]));
            }
        }
예제 #2
0
        /// <summary>
        /// Converts the bind pose matrices to SRT transforms.
        /// </summary>
        /// <param name="bindPoses">The bind pose matrices.</param>
        /// <returns>The equivalent SRT transforms.</returns>
        private static IList <SrtTransform> ConvertToSrt(IList <Matrix> bindPoses)
        {
            var numberOfBones = bindPoses.Count;
            var result        = new SrtTransform[numberOfBones];

            for (int i = 0; i < numberOfBones; i++)
            {
                result[i] = SrtTransform.FromMatrix(bindPoses[i]);
            }

            return(result);
        }
        private void BuildSkeleton()
        {
            // Get an array of all bones in depth-first order.
            // (Same as MeshHelper.FlattenSkeleton(root).)
            var bones = TreeHelper.GetSubtree(_rootBone, n => n.Children.OfType <BoneContent>(), true)
                        .ToList();

            // Create list of parent indices, bind pose transformations and bone names.
            var boneParents      = new List <int>();
            var bindTransforms   = new List <SrtTransform>();
            var boneNames        = new List <string>();
            int numberOfWarnings = 0;

            foreach (var bone in bones)
            {
                int parentIndex = bones.IndexOf(bone.Parent as BoneContent);
                boneParents.Add(parentIndex);

                // Log warning for invalid transform matrices - but not too many warnings.
                if (numberOfWarnings < 2)
                {
                    if (!SrtTransform.IsValid((Matrix)bone.Transform))
                    {
                        if (numberOfWarnings < 1)
                        {
                            _context.Logger.LogWarning(null, _input.Identity, "Bone transform is not supported. Bone transform matrices may only contain scaling, rotation and translation.");
                        }
                        else
                        {
                            _context.Logger.LogWarning(null, _input.Identity, "More unsupported bone transform found.");
                        }

                        numberOfWarnings++;
                    }
                }

                bindTransforms.Add(SrtTransform.FromMatrix(bone.Transform));

                if (boneNames.Contains(bone.Name))
                {
                    string message = String.Format(CultureInfo.InvariantCulture, "Duplicate bone name (\"{0}\") found.", bone.Name);
                    throw new InvalidContentException(message, _input.Identity);
                }

                boneNames.Add(bone.Name);
            }

            // Create and return a new skeleton instance.
            _skeleton = new Skeleton(boneParents, boneNames, bindTransforms);
        }
예제 #4
0
        public void FromToMatrixTest()
        {
            var t = new Vector3F(1, 2, 3);
            var r = new QuaternionF(1, 2, 3, 4).Normalized;
            var s = new Vector3F(2, 7, 9);
            var m = Matrix44F.CreateTranslation(t) * Matrix44F.CreateRotation(r) * Matrix44F.CreateScale(s);

            var srt = SrtTransform.FromMatrix(m);

            Assert.IsTrue(Vector3F.AreNumericallyEqual(t, srt.Translation));
            Assert.IsTrue(QuaternionF.AreNumericallyEqual(r, srt.Rotation));
            Assert.IsTrue(Vector3F.AreNumericallyEqual(s, srt.Scale));

            // XNA:
            srt = SrtTransform.FromMatrix((Matrix)m);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(t, srt.Translation));
            Assert.IsTrue(QuaternionF.AreNumericallyEqual(r, srt.Rotation));
            Assert.IsTrue(Vector3F.AreNumericallyEqual(s, srt.Scale));

            // With negative scale, the decomposition is not unique (many possible combinations of
            // axis mirroring + rotation).
            t   = new Vector3F(1, 2, 3);
            r   = new QuaternionF(1, 2, 3, 4).Normalized;
            s   = new Vector3F(2, -7, 9);
            m   = Matrix44F.CreateTranslation(t) * Matrix44F.CreateRotation(r) * Matrix44F.CreateScale(s);
            srt = SrtTransform.FromMatrix(m);
            var m2 = (Matrix44F)srt;

            Assert.IsTrue(Matrix44F.AreNumericallyEqual(m, m2));

            m2 = srt.ToMatrix44F();
            Assert.IsTrue(Matrix44F.AreNumericallyEqual(m, m2));

            m2 = srt;
            Assert.IsTrue(Matrix44F.AreNumericallyEqual(m, m2));

            Matrix mXna = srt.ToXna();

            Assert.IsTrue(Matrix44F.AreNumericallyEqual(m, (Matrix44F)mXna));

            mXna = srt;
            Assert.IsTrue(Matrix44F.AreNumericallyEqual(m, (Matrix44F)mXna));
        }
예제 #5
0
        //--------------------------------------------------------------
        #region Avatar Skeleton Animation
        //--------------------------------------------------------------

        /// <summary>
        /// Converts an intermediate-format content pipeline AnimationContent object
        /// to a SkeletonKeyFrameAnimation.
        /// </summary>
        private SkeletonKeyFrameAnimation ProcessSkeletonAnimation(AnimationContent animationContent, ContentProcessorContext context)
        {
            if (animationContent.Duration <= TimeSpan.Zero)
            {
                throw new InvalidContentException("Animation has a zero duration.", animationContent.Identity);
            }

            // Create a SkeletonPose key frame animation that will animate the SkeletonPose
            // property of an AvatarPose.
            var animation = new SkeletonKeyFrameAnimation
            {
                TargetProperty = "SkeletonPose",
            };

            // Process each channel in the animation
            int numberOfKeyFrames = 0;

            foreach (KeyValuePair <string, AnimationChannel> item in animationContent.Channels)
            {
                var channelName = item.Key;
                channelName = CleanBoneName(channelName);

                var channel = item.Value;

                // Don't add animation nodes with "_END" in the name
                // -- These bones were removed from the skeleton already
                if (channelName.Contains("_END"))
                {
                    continue;
                }

                // Look up what bone this channel is controlling.
                int boneIndex;
                if (!_boneNames.TryGetValue(channelName, out boneIndex))
                {
                    var message = string.Format("Found animation for bone '{0}', which is not part of the skeleton.", channelName);
                    throw new InvalidContentException(message, animationContent.Identity);
                }

                // Convert and add the key frame data.
                foreach (AnimationKeyframe keyframe in channel)
                {
                    var time   = keyframe.Time;
                    var matrix = CreateKeyFrameMatrix(keyframe, boneIndex);
                    var srt    = SrtTransform.FromMatrix(matrix);
                    animation.AddKeyFrame(boneIndex, time, srt);

                    numberOfKeyFrames++;
                }
            }

            if (numberOfKeyFrames == 0)
            {
                throw new InvalidContentException("Animation has no key frames.", animationContent.Identity);
            }

            // Compress animation to safe memory.
            float removedKeyFrames = animation.Compress(
                CompressionScaleThreshold,
                CompressionRotationThreshold,
                CompressionTranslationThreshold);

            if (removedKeyFrames > 0)
            {
                context.Logger.LogImportantMessage("Compression removed {0:P} of all key frames.", removedKeyFrames);
            }

            // Finalize the skeleton key frame animation. This optimizes the internal data structures.
            animation.Freeze();

            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,
            });
        }
        private SkeletonKeyFrameAnimation BuildAnimation(AnimationContent animationContent)
        {
            string name = animationContent.Name;

            // Add loop frame?
            bool addLoopFrame = false;

            if (_modelDescription != null)
            {
                var animationDescription = _modelDescription.Animation;
                if (animationDescription != null)
                {
                    addLoopFrame = animationDescription.AddLoopFrame ?? false;

                    if (animationDescription.Splits != null)
                    {
                        foreach (var split in animationDescription.Splits)
                        {
                            if (split.Name == name)
                            {
                                if (split.AddLoopFrame.HasValue)
                                {
                                    addLoopFrame = split.AddLoopFrame.Value;
                                }

                                break;
                            }
                        }
                    }
                }
            }

            var animation = new SkeletonKeyFrameAnimation {
                EnableInterpolation = true
            };

            // Process all animation channels (each channel animates a bone).
            int numberOfKeyFrames = 0;

            foreach (var item in animationContent.Channels)
            {
                string           channelName = item.Key;
                AnimationChannel channel     = item.Value;

                int boneIndex = _skeleton.GetIndex(channelName);
                if (boneIndex != -1)
                {
                    SrtTransform?loopFrame = null;

                    var bindPoseRelativeInverse = _skeleton.GetBindPoseRelative(boneIndex).Inverse;
                    foreach (AnimationKeyframe keyframe in channel)
                    {
                        TimeSpan     time      = keyframe.Time;
                        SrtTransform transform = SrtTransform.FromMatrix(keyframe.Transform);

                        // The matrix in the key frame is the transformation in the coordinate space of the
                        // parent bone. --> Convert it to a transformation relative to the animated bone.
                        transform = bindPoseRelativeInverse * transform;

                        // To start with minimal numerical errors, we normalize the rotation quaternion.
                        transform.Rotation.Normalize();

                        if (loopFrame == null)
                        {
                            loopFrame = transform;
                        }

                        if (!addLoopFrame || time < animationContent.Duration)
                        {
                            animation.AddKeyFrame(boneIndex, time, transform);
                        }

                        numberOfKeyFrames++;
                    }

                    if (addLoopFrame && loopFrame.HasValue)
                    {
                        animation.AddKeyFrame(boneIndex, animationContent.Duration, loopFrame.Value);
                    }
                }
                else
                {
                    _context.Logger.LogWarning(
                        null, animationContent.Identity,
                        "Found animation for bone \"{0}\", which is not part of the skeleton.",
                        channelName);
                }
            }

            if (numberOfKeyFrames == 0)
            {
                _context.Logger.LogWarning(null, animationContent.Identity, "Animation is ignored because it has no keyframes.");
                return(null);
            }

            // Compress animation to save memory.
            if (_modelDescription != null)
            {
                var animationDescription = _modelDescription.Animation;
                if (animationDescription != null)
                {
                    float removedKeyFrames = animation.Compress(
                        animationDescription.ScaleCompression,
                        animationDescription.RotationCompression,
                        animationDescription.TranslationCompression);

                    if (removedKeyFrames > 0)
                    {
                        _context.Logger.LogImportantMessage("{0}: Compression removed {1:P} of all key frames.",
                                                            string.IsNullOrEmpty(name) ? "Unnamed" : name,
                                                            removedKeyFrames);
                    }
                }
            }

            // Finalize the animation. (Optimizes the animation data for fast runtime access.)
            animation.Freeze();

            return(animation);
        }