예제 #1
0
        public void MissingWeights()
        {
            var animation = new SkeletonKeyFrameAnimation();

              animation.AddKeyFrame(0, TimeSpan.FromSeconds(0), new SrtTransform(QuaternionF.Identity));
              animation.AddKeyFrame(1, TimeSpan.FromSeconds(0), new SrtTransform(QuaternionF.Identity));

              Assert.AreEqual(1.0f, animation.GetWeight(0));
              Assert.AreEqual(1.0f, animation.GetWeight(1));

              animation.Freeze();

              Assert.AreEqual(1.0f, animation.GetWeight(0));
        }
예제 #2
0
    public void MissingWeights()
    {
      var animation = new SkeletonKeyFrameAnimation();

      animation.AddKeyFrame(0, TimeSpan.FromSeconds(0), new SrtTransform(Quaternion.Identity));
      animation.AddKeyFrame(1, TimeSpan.FromSeconds(0), new SrtTransform(Quaternion.Identity));

      Assert.AreEqual(1.0f, animation.GetWeight(0));
      Assert.AreEqual(1.0f, animation.GetWeight(1));

      animation.Freeze();

      Assert.AreEqual(1.0f, animation.GetWeight(0));
    }
예제 #3
0
        public DudeWalkingSample(Microsoft.Xna.Framework.Game game)
            : base(game)
        {
            var modelNode = ContentManager.Load <ModelNode>("Dude/Dude");
            var meshNode  = modelNode.GetSubtree().OfType <MeshNode>().First().Clone();

            meshNode.PoseLocal = new Pose(new Vector3F(0, 0, 0), Matrix33F.CreateRotationY(ConstantsF.Pi));
            SampleHelper.EnablePerPixelLighting(meshNode);
            GraphicsScreen.Scene.Children.Add(meshNode);

            // The imported animations are stored in the mesh.
            Dictionary <string, SkeletonKeyFrameAnimation> animations = meshNode.Mesh.Animations;

            // The Dude model contains only one animation, which is a SkeletonKeyFrameAnimation with
            // a walk cycle.
            SkeletonKeyFrameAnimation walkAnimation = animations.Values.First();

            // Wrap the walk animation in an animation clip that loops the animation forever.
            AnimationClip <SkeletonPose> loopingAnimation = new AnimationClip <SkeletonPose>(walkAnimation)
            {
                LoopBehavior = LoopBehavior.Cycle,
                Duration     = TimeSpan.MaxValue,
            };

            // Start the animation and keep the created AnimationController.
            // We must cast the SkeletonPose to IAnimatableProperty because SkeletonPose implements
            // IAnimatableObject and IAnimatableProperty. We must tell the AnimationService if we want
            // to animate an animatable property of the SkeletonPose (IAnimatableObject), or if we want to
            // animate the whole SkeletonPose (IAnimatableProperty).
            _animationController = AnimationService.StartAnimation(loopingAnimation, (IAnimatableProperty)meshNode.SkeletonPose);

            // The animation will be applied the next time AnimationManager.ApplyAnimations() is called
            // in the main loop. ApplyAnimations() is called before this method is called, therefore
            // the model will be rendered in the bind pose in this frame and in the first animation key
            // frame in the next frame - this creates an annoying visual popping effect.
            // We can avoid this if we call AnimationController.UpdateAndApply(). This will immediately
            // change the model pose to the first key frame pose.
            _animationController.UpdateAndApply();

            // (Optional) Enable Auto-Recycling:
            // After the animation is stopped, the animation service will recycle all
            // intermediate data structures.
            _animationController.AutoRecycle();
        }
예제 #4
0
        public CompressionSample(Microsoft.Xna.Framework.Game game)
            : base(game)
        {
            var modelNode = ContentManager.Load <ModelNode>("Dude/Dude");

            SampleHelper.EnablePerPixelLighting(modelNode);

            _meshNodeUncompressed           = modelNode.GetSubtree().OfType <MeshNode>().First().Clone();
            _meshNodeUncompressed.PoseLocal = new Pose(new Vector3F(-0.5f, 0, 0), Matrix33F.CreateRotationY(ConstantsF.Pi));
            GraphicsScreen.Scene.Children.Add(_meshNodeUncompressed);

            _meshNodeCompressed           = _meshNodeUncompressed.Clone();
            _meshNodeCompressed.PoseLocal = new Pose(new Vector3F(0.5f, 0, 0), Matrix33F.CreateRotationY(ConstantsF.Pi));
            GraphicsScreen.Scene.Children.Add(_meshNodeCompressed);

            Dictionary <string, SkeletonKeyFrameAnimation> animations = _meshNodeUncompressed.Mesh.Animations;

            _animation = animations.Values.First();

            RestartAnimations();
        }
예제 #5
0
        //--------------------------------------------------------------
        #region General Methods
        //--------------------------------------------------------------

        public override TimelineGroup Process(NodeContent input, ContentProcessorContext context)
        {
            // Uncomment this to attach and launch a debugger.
            //System.Diagnostics.Debugger.Launch();

            // Get the skeleton node.
            NodeContent skeleton = FindSkeleton(input);

            if (skeleton == null)
            {
                throw new InvalidContentException("Avatar skeleton not found.", input.Identity);
            }
            if (skeleton.Animations.Count < 1)
            {
                throw new InvalidContentException("No animation was found in the file.", input.Identity);
            }
            if (skeleton.Animations.Count > 1)
            {
                throw new InvalidContentException("More than one animation was found.", input.Identity);
            }

            // Remove the extra bones that we are not using.
            RemoveEndBonesAndFixBoneNames(skeleton);

            // Create a list of the bones from the skeleton hierarchy.
            IList <NodeContent> bones = FlattenSkeleton(skeleton);

            if (bones.Count != AvatarRenderer.BoneCount)
            {
                throw new InvalidContentException("Invalid number of bones found.", input.Identity);
            }

            // Fill the bind pose array with the transforms from the bones.
            foreach (NodeContent bone in bones)
            {
                _bindPoses.Add(bone.Transform);
            }

            // Build up a table mapping bone names to indices.
            _boneNames = new Dictionary <string, int>();
            for (int i = 0; i < bones.Count; i++)
            {
                string boneName = bones[i].Name;
                if (!string.IsNullOrEmpty(boneName))
                {
                    _boneNames.Add(boneName, i);
                }
            }

            // Create the custom animation data.
            // From the error-checking above, we know there will only be one animation.
            AnimationContent                  animationContent    = skeleton.Animations.Values.First();
            SkeletonKeyFrameAnimation         skeletonAnimation   = ProcessSkeletonAnimation(animationContent, context);
            AvatarExpressionKeyFrameAnimation expressionAnimation = ProcessExpressionAnimation(input, context);

            var timelineGroup = new TimelineGroup();

            if (skeletonAnimation != null)
            {
                timelineGroup.Add(skeletonAnimation);
            }
            if (expressionAnimation != null)
            {
                timelineGroup.Add(expressionAnimation);
            }

            return(timelineGroup);
        }
예제 #6
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);
        }
        public FacialAnimationSample(Microsoft.Xna.Framework.Game game)
            : base(game)
        {
            _graphicsScreen = new DeferredGraphicsScreen(Services)
            {
                DrawReticle = false
            };
            GraphicsService.Screens.Insert(0, _graphicsScreen);
            Services.Register(typeof(DebugRenderer), null, _graphicsScreen.DebugRenderer);
            Services.Register(typeof(IScene), null, _graphicsScreen.Scene);

            // Add a game object which adds some GUI controls for the deferred graphics
            // screen to the Options window.
            GameObjectService.Objects.Add(new DeferredGraphicsOptionsObject(Services));

            // Use a fixed camera.
            var projection = new PerspectiveProjection();

            projection.SetFieldOfView(
                ConstantsF.PiOver4,
                GraphicsService.GraphicsDevice.Viewport.AspectRatio,
                0.1f,
                10);
            var cameraNode = new CameraNode(new Camera(projection));

            cameraNode.LookAt(new Vector3F(0.15f, 0.15f, 0.5f), new Vector3F(0.1f, 0.15f, 0), Vector3F.Up);
            _graphicsScreen.Scene.Children.Add(cameraNode);
            _graphicsScreen.ActiveCameraNode = cameraNode;

            // Lighting setup:
            var keyLight = new LightNode(new Spotlight {
                DiffuseIntensity = 0.6f, SpecularIntensity = 0.4f
            });

            keyLight.LookAt(new Vector3F(-2, 2, 2), new Vector3F(), Vector3F.Up);
            _graphicsScreen.Scene.Children.Add(keyLight);

            var backLight = new LightNode(new Spotlight {
                DiffuseIntensity = 0.3f, SpecularIntensity = 0.3f
            });

            backLight.LookAt(new Vector3F(1, 0.5f, -2), new Vector3F(), Vector3F.Up);
            _graphicsScreen.Scene.Children.Add(backLight);

            var fillLight = new LightNode(new AmbientLight {
                HemisphericAttenuation = 1, Intensity = 0.1f
            });

            _graphicsScreen.Scene.Children.Add(fillLight);

            // The scene does not have a proper background. That's why the exposure is a
            // bit off. --> Reduce the max exposure.
            var hdrFilter = _graphicsScreen.PostProcessors.OfType <HdrFilter>().First();

            hdrFilter.MaxExposure = 6;

            // Load the customized "Sintel" model (original: Durian Open Movie Project - http://www.sintel.org/).
            var model = ContentManager.Load <ModelNode>("Sintel/Sintel-Head").Clone();

            model.PoseWorld = new Pose(new Vector3F(0, 0, 0), Matrix33F.CreateRotationY(MathHelper.ToRadians(10)) * Matrix33F.CreateRotationX(-MathHelper.ToRadians(90)));
            _graphicsScreen.Scene.Children.Add(model);

            // The model consists of a root node and a mesh node.
            //  ModelNode "Sintel-Head"
            //    MeshNode "Sintel"
            _sintel = (MeshNode)model.Children[0];

            // The model contains two skeletal animations:
            // - "MOUTH-open" is just a single frame.
            // - "Test" is a short animation (250 frames).

            // In the Options window, we will add a slider to move the jaw.
            // Slider.Value = 0 ... mouth closed (default)
            _mouthClosedPose = SkeletonPose.Create(_sintel.Mesh.Skeleton);
            // Slider.Value = 1 ... mouth open (copied from the "MOUTH-open" animation)
            SkeletonKeyFrameAnimation mouthOpen = _sintel.Mesh.Animations["MOUTH-open"];

            _mouthOpenPose = SkeletonPose.Create(_sintel.Mesh.Skeleton);
            mouthOpen.GetValue(TimeSpan.Zero, ref _mouthOpenPose, ref _mouthOpenPose, ref _mouthOpenPose);

            // Turn the "Test" animation into an endless loop.
            _skeletalAnimation = new AnimationClip <SkeletonPose>(_sintel.Mesh.Animations["Test"])
            {
                Duration     = TimeSpan.MaxValue,
                LoopBehavior = LoopBehavior.Cycle
            };

            // Mesh has several morph targets for facial animation, which are imported
            // automatically via the content pipeline. Unfortunately, the XNA content
            // pipeline cannot import morph target animations automatically.
            // In this demo, we will create a morph target animation in code.
            _morphingAnimation = CreateMorphingAnimation();

            CreateGuiControls();
        }
        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);
        }