Exemple #1
0
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContent
        /// object to our runtime AnimationClip format.
        /// </summary>
        public static ModelAnimationClip ProcessRootAnimation(AnimationContent animation, string name)
        {
            List <ModelKeyframe> keyframes = new List <ModelKeyframe>();

            // The root animation is controlling the root of the bones
            AnimationChannel channel = animation.Channels[name];

            // Add the transformations on the root of the model
            foreach (AnimationKeyframe keyframe in channel)
            {
                keyframes.Add(new ModelKeyframe(0, keyframe.Time, keyframe.Transform));
            }

            // Sort the merged keyframes by time.
            keyframes.Sort(CompareKeyframeTimes);

            if (keyframes.Count == 0)
            {
                throw new InvalidContentException("Animation has no keyframes.");
            }

            if (animation.Duration <= TimeSpan.Zero)
            {
                throw new InvalidContentException("Animation has a zero duration.");
            }

            return(new ModelAnimationClip(animation.Duration, keyframes));
        }
Exemple #2
0
        static SerializableAnimation ProcessAnimation(AnimationContent xnaAnimation)
        {
            SerializableAnimation animation = new SerializableAnimation();

            animation.name   = xnaAnimation.Name;
            animation.length = (float)xnaAnimation.Duration.TotalSeconds;

            foreach (KeyValuePair <string, AnimationChannel> xnaAnimationChannelPair in xnaAnimation.Channels)
            {
                AnimationChannel xnaAnimationChannel     = xnaAnimationChannelPair.Value;
                string           xnaAnimationChannelName = xnaAnimationChannelPair.Key;

                SerializableTrack track = new SerializableTrack();
                track.name = xnaAnimationChannelName;
                animation.tracks.Add(track);

                for (int i = 0; i < xnaAnimationChannel.Count; i++)
                {
                    AnimationKeyframe    xnaKeyframe = xnaAnimationChannel[i];
                    SerializableKeyFrame keyframe    = new SerializableKeyFrame((float)xnaKeyframe.Time.TotalSeconds, xnaKeyframe.Transform);
                    track.AddKeyFrame(keyframe);
                }
            }

            return(animation);
        }
Exemple #3
0
        private glTFLoader.Schema.Animation CreateAnimation(AoMEngineLibrary.Graphics.Model.Animation animation, int weightCount, Stream bufferStream)
        {
            var sampler = new AnimationSampler();

            sampler.Interpolation = AnimationSampler.InterpolationEnum.LINEAR;

            CreateKeysBuffer(animation, bufferStream);
            sampler.Input = accessors.Count - 1;

            CreateWeightsBuffer(animation, weightCount, bufferStream);
            sampler.Output = accessors.Count - 1;

            var target = new AnimationChannelTarget();

            target.Node = 0;
            target.Path = AnimationChannelTarget.PathEnum.weights;

            var channel = new AnimationChannel();

            channel.Sampler = 0;
            channel.Target  = target;

            var gltfAnimation = new glTFLoader.Schema.Animation();

            gltfAnimation.Samplers = new[] { sampler };
            gltfAnimation.Channels = new[] { channel };

            return(gltfAnimation);
        }
Exemple #4
0
 public void SelectChannel(string name)
 {
     //Debug.Log ("Selecting channel. Available channels: " + channels.Keys.Count);
     currentChannel = channels[name];
     totalFrames    = currentChannel.sprites.Length;
     //Reset();
 }
Exemple #5
0
        public void TestFitting()
        {
            // Make a sinus between T = 0s to 10s at 60 FPS
            var animationChannel = new AnimationChannel();

            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.Zero, Value = 0.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(10.0), Value = 0.0f
            });

            var maxErrorThreshold = 0.05f;
            var timeStep          = CompressedTimeSpan.FromSeconds(1.0f / 60.0f);
            Func <CompressedTimeSpan, float> curve = x =>
            {
                if (x.Ticks == 196588)
                {
                }
                return((float)Math.Sin(x.Ticks / (double)CompressedTimeSpan.FromSeconds(10.0).Ticks *Math.PI * 2.0));
            };

            animationChannel.Fitting(
                curve,
                CompressedTimeSpan.FromSeconds(1.0f / 60.0f),
                maxErrorThreshold);

            var evaluator = new AnimationChannel.Evaluator(animationChannel.KeyFrames);

            for (var time = CompressedTimeSpan.Zero; time < CompressedTimeSpan.FromSeconds(10.0); time += timeStep)
            {
                var diff = Math.Abs(curve(time) - evaluator.Evaluate(time));
                Assert.That(diff, Is.LessThanOrEqualTo(maxErrorThreshold));
            }
        }
        public void TestFitting()
        {
            // Make a sinus between T = 0s to 10s at 60 FPS
            var animationChannel = new AnimationChannel();
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.Zero, Value = 0.0f });
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.FromSeconds(10.0), Value = 0.0f });

            var maxErrorThreshold = 0.05f;
            var timeStep = CompressedTimeSpan.FromSeconds(1.0f / 60.0f);
            Func<CompressedTimeSpan, float> curve = x =>
                {
                    if (x.Ticks == 196588)
                    {
                    }
                    return (float)Math.Sin(x.Ticks / (double)CompressedTimeSpan.FromSeconds(10.0).Ticks * Math.PI * 2.0);
                };
            animationChannel.Fitting(
                curve,
                CompressedTimeSpan.FromSeconds(1.0f / 60.0f),
                maxErrorThreshold);

            var evaluator = new AnimationChannel.Evaluator(animationChannel.KeyFrames);
            for (var time = CompressedTimeSpan.Zero; time < CompressedTimeSpan.FromSeconds(10.0); time += timeStep)
            {
                var diff = Math.Abs(curve(time) - evaluator.Evaluate(time));
                Assert.That(diff, Is.LessThanOrEqualTo(maxErrorThreshold));
            }
        }
        } // ProcessRigidAnimation

        static void AddTransformationNodes(string animationName, Dictionary<string, int> boneMap, NodeContent input, List<ModelKeyframe> keyframes, ref TimeSpan duration)
        {
            // Add the transformation on each of the meshes
            foreach (NodeContent childNode in input.Children)
            {
                // If this node doesn't have keyframes for this animation we should just skip it
                if (childNode.Animations.ContainsKey(animationName))
                {
                    AnimationChannel childChannel = childNode.Animations[animationName].Channels[childNode.Name];
                    if (childNode.Animations[animationName].Duration != duration)
                    {
                        if (duration < childNode.Animations[animationName].Duration)
                            duration = childNode.Animations[animationName].Duration;
                    }

                    int boneIndex;
                    if (!boneMap.TryGetValue(childNode.Name, out boneIndex))
                    {
                        throw new InvalidContentException(string.Format("Found animation for bone '{0}', which is not part of the model.", childNode.Name));
                    }

                    foreach (AnimationKeyframe keyframe in childChannel)
                    {
                        keyframes.Add(new ModelKeyframe((ushort)boneIndex, (float)(keyframe.Time.TotalSeconds), keyframe.Transform));
                    }
                }

                AddTransformationNodes(animationName, boneMap, childNode, keyframes, ref duration);
            }
        } // AddTransformationNodes
        /// <summary>
        /// Retrieves and interpolates the pose of an animation channel.
        /// </summary>
        /// <param name="animationChannel">Name of the animation channel.</param>
        /// <param name="animationTime">Current animation clip time.</param>
        /// <param name="outPose">The output interpolated pose.</param>
        private void InterpolateChannelPose(AnimationChannel animationChannel, TimeSpan animationTime,
                                            out Pose outPose)
        {
            if (translationInterpolation == InterpolationMode.None &&
                orientationInterpolation == InterpolationMode.None &&
                scaleInterpolation == InterpolationMode.None)
            {
                int keyframeIndex = animationChannel.GetKeyframeIndexByTime(animationTime);
                outPose = animationChannel[keyframeIndex].Pose;
            }
            else
            {
                int keyframeIndex = animationChannel.GetKeyframeIndexByTime(animationTime);
                int nextKeyframeIndex;

                // If we are looping then the next frame may wrap around to
                // the beginning. If not we should just clamp it at the last frame
                if (loopEnabled)
                {
                    nextKeyframeIndex = (keyframeIndex + 1) % animationChannel.Count;
                }
                else
                {
                    nextKeyframeIndex = Math.Min(keyframeIndex + 1, animationChannel.Count - 1);
                }

                AnimationChannelKeyframe keyframe1 = animationChannel[keyframeIndex];
                AnimationChannelKeyframe keyframe2 = animationChannel[nextKeyframeIndex];

                // Calculate the time between the keyframes considering loop
                long keyframeDuration;
                if (keyframeIndex == (animationChannel.Count - 1))
                {
                    keyframeDuration = animationClip.Duration.Ticks - keyframe1.Time.Ticks;
                }
                else
                {
                    keyframeDuration = keyframe2.Time.Ticks - keyframe1.Time.Ticks;
                }

                // Interpolate when duration higher than zero
                if (keyframeDuration > 0)
                {
                    long  elapsedKeyframeTime = animationTime.Ticks - keyframe1.Time.Ticks;
                    float lerpFactor          = elapsedKeyframeTime / (float)keyframeDuration;

                    outPose =
                        Pose.Interpolate(keyframe1.Pose, keyframe2.Pose, lerpFactor,
                                         translationInterpolation, orientationInterpolation, scaleInterpolation);
                }
                // Otherwise don't interpolate
                else
                {
                    outPose = keyframe1.Pose;
                }
            }
        }
        private void LoadAnimation(GLTF.Schema.Animation gltfAnimation, int index, AnimationClip clip)
        {
            clip.name = gltfAnimation.Name != null && gltfAnimation.Name.Length > 0 ? gltfAnimation.Name : "GLTFAnimation_" + index;
            for (int i = 0; i < gltfAnimation.Channels.Count; ++i)
            {
                AnimationChannel channel = gltfAnimation.Channels[i];
                addGLTFChannelDataToClip(gltfAnimation.Channels[i], clip);
            }

            clip.EnsureQuaternionContinuity();
        }
Exemple #10
0
        // template AnimationSet
        // {
        //     [ Animation ]
        // }
        /// <summary>
        /// Imports an animation set that is added to the AnimationContentDictionary of
        /// the root frame.
        /// </summary>
        private void ImportAnimationSet()
        {
            AnimationContent animSet = new AnimationContent();

            animSet.Name = tokens.ReadName();
            // Give each animation a unique name
            if (animSet.Name == null)
            {
                animSet.Name = "Animation" + root.Animations.Count.ToString();
            }

            // Fill in all the channels of the animation.  Each channel refers to
            // a single bone's role in the animation.
            for (string next = tokens.NextToken(); next != "}"; next = tokens.NextToken())
            {
                if (next == "Animation")
                {
                    string           boneName;
                    AnimationChannel anim = ImportAnimationChannel(out boneName);
                    // Every channel must be attached to a bone!
                    if (boneName == null)
                    {
                        throw new Exception("Animation in file is not attached to any joint");
                    }
                    // Make sure that the duration of the animation is set to the
                    // duration of the longest animation channel
                    if (anim[anim.Count - 1].Time > animSet.Duration)
                    {
                        animSet.Duration = anim[anim.Count - 1].Time;
                    }
                    animSet.Channels.Add(boneName, anim);
                }
                // skip nodes we are uninterested in
                else if (next == "{")
                {
                    tokens.SkipNode();
                }
            }
            //skeletonRoot = MeshHelper.FindSkeleton(root);
            //skeletonRoot.Animations.Add(animSet.Name, animSet);
            if (root.Animations.ContainsKey(animSet.Name))
            {
                string error = "Attempting to add " + animSet.Name + " but it already exists.";



                throw new Exception(error);
            }
            root.Animations.Add(animSet.Name, animSet);
        }
Exemple #11
0
        private AnimationContent CreateAnimation(Assimp.Animation aiAnimation)
        {
            var animation = new AnimationContent
            {
                Name     = FixupAnimationName(aiAnimation.Name),
                Duration = TimeSpan.FromSeconds(aiAnimation.DurationInTicks / aiAnimation.TicksPerSecond)
            };

            foreach (var aiChannel in aiAnimation.NodeAnimationChannels)
            {
                var channel = new AnimationChannel();

                // We can have different numbers of keyframes for each, so find the max index.
                var keyCount = Math.Max(aiChannel.PositionKeyCount, Math.Max(aiChannel.RotationKeyCount, aiChannel.ScalingKeyCount));

                // Get all unique keyframe times
                var times = aiChannel.PositionKeys.Select(k => k.Time)
                            .Union(aiChannel.RotationKeys.Select(k => k.Time))
                            .Union(aiChannel.ScalingKeys.Select(k => k.Time))
                            .Distinct().ToList();

                // The rest of this loop is almost certainly wrong. Don't trust it.
                // There's some magical combination, ordering, or transposition we have
                // to figure out to translate FBX->Assimp->XNA.
                // Possibilities: matrix offset transform, missing a base transform, an extra base transform, etc.

                var toBoneSpace = _objectToBone.ContainsKey(aiChannel.NodeName)
                    ? _objectToBone[aiChannel.NodeName] * _skeletonRoot.Transform
                    : _skeletonRoot.Transform;

                foreach (var aiKeyTime in times)
                {
                    var time          = TimeSpan.FromSeconds(aiKeyTime / aiAnimation.TicksPerSecond);
                    var translation   = Matrix4x4.FromTranslation(aiChannel.PositionKeys.FirstOrDefault(k => k.Time == aiKeyTime).Value);
                    var rotation      = new Matrix4x4(aiChannel.RotationKeys.FirstOrDefault(k => k.Time == aiKeyTime).Value.GetMatrix());
                    var scale         = Matrix4x4.FromScaling(aiChannel.ScalingKeys.FirstOrDefault(k => k.Time == aiKeyTime).Value);
                    var nodeTransform = translation * rotation * scale;

                    var xform = toBoneSpace * nodeTransform * _globalInverseXform;
                    channel.Add(new AnimationKeyframe(time, ToXna(xform)));
                }

                animation.Channels.Add(aiChannel.NodeName, channel);
            }

            return(animation);
        }
        } // AddAnimationNodes

        #endregion

        #region Process Root Animation

        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContent
        /// object to our runtime AnimationClip format.
        /// </summary>
        internal static RootAnimationClip ProcessRootAnimation(AnimationContent animation, string name)
        {
            List<RootKeyframe> keyframes = new List<RootKeyframe>();

            // The root animation is controlling the root of the bones
            AnimationChannel channel = animation.Channels[name];
            
            // Add the transformations on the root of the model
            foreach (AnimationKeyframe keyframe in channel)
            {
                keyframes.Add(new RootKeyframe((float)(keyframe.Time.TotalSeconds), keyframe.Transform));
            }            

            // Sort the merged keyframes by time.
            keyframes.Sort(CompareKeyframeTimes);

            #region Key Frame Reduction

            // We drop key frame data where the bone transformation is equal to the previous key frame.
            List<RootKeyframe> keyframesReduced = new List<RootKeyframe>();
            keyframesReduced.Add(keyframes[0]);
            for (int i = 1; i < keyframes.Count; i++)
            {
                if (keyframes[i - 1].Transform != keyframes[i].Transform)
                {
                    keyframesReduced.Add(keyframes[i]);
                }
            }
            keyframes = keyframesReduced;
            // Sort the merged keyframes by time.
            keyframes.Sort(CompareKeyframeTimes);

            #endregion

            if (keyframes.Count == 0)
                throw new InvalidContentException("Animation has no keyframes.");

            if (animation.Duration <= TimeSpan.Zero)
                throw new InvalidContentException("Animation has a zero duration.");

            RootKeyframe[] keyframesArray = new RootKeyframe[keyframes.Count];
            for (int i = 0; i < keyframes.Count; i++)
            {
                keyframesArray[i] = keyframes[i];
            }
            return new RootAnimationClip((float)(animation.Duration.TotalSeconds), keyframesArray);
        } // ProcessRootAnimation
Exemple #13
0
        /// <summary>
        /// Retrieves and interpolates the pose of an animation channel.
        /// </summary>
        /// <param name="animationChannel">Name of the animation channel.</param>
        /// <param name="animationTime">Current animation clip time.</param>
        /// <param name="outPose">The output interpolated pose.</param>
        private void InterpolateChannelPose(AnimationChannel animationChannel, TimeSpan animationTime,
                                            out Pose outPose)
        {
            if (translationInterpolation == InterpolationMode.None &&
                orientationInterpolation == InterpolationMode.None &&
                scaleInterpolation == InterpolationMode.None)
            {
                int keyframeIndex = animationChannel.GetKeyframeIndexByTime(animationTime);
                outPose = animationChannel[keyframeIndex].Pose;
            }
            else
            {
                int keyframeIndex     = animationChannel.GetKeyframeIndexByTime(animationTime);
                int nextKeyframeIndex = (keyframeIndex + 1) % animationChannel.Count;

                AnimationChannelKeyframe keyframe1 = animationChannel[keyframeIndex];
                AnimationChannelKeyframe keyframe2 = animationChannel[nextKeyframeIndex];

                // Calculate the time between the keyframes considering loop
                long keyframeDuration;
                if (keyframeIndex == (animationChannel.Count - 1))
                {
                    keyframeDuration = animationClip.Duration.Ticks - keyframe1.Time.Ticks;
                }
                else
                {
                    keyframeDuration = keyframe2.Time.Ticks - keyframe1.Time.Ticks;
                }

                // Interpolate when duration higher than zero
                if (keyframeDuration > 0)
                {
                    long  elapsedKeyframeTime = animationTime.Ticks - keyframe1.Time.Ticks;
                    float lerpFactor          = elapsedKeyframeTime / (float)keyframeDuration;

                    outPose =
                        Pose.Interpolate(keyframe1.Pose, keyframe2.Pose, lerpFactor,
                                         translationInterpolation, orientationInterpolation, scaleInterpolation);
                }
                // Otherwise don't interpolate
                else
                {
                    outPose = keyframe1.Pose;
                }
            }
        }
Exemple #14
0
        public void TestDiscontinuity()
        {
            var animationChannel = new AnimationChannel();

            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.Zero, Value = 0.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(1.0), Value = 0.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(1.0), Value = 0.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(1.0), Value = 1.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(1.0), Value = 1.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(2.0), Value = 1.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(2.0), Value = 1.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(2.0), Value = 0.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(2.0), Value = 0.0f
            });

            var evaluator = new AnimationChannel.Evaluator(animationChannel.KeyFrames);

            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(0.0)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(0.999999)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(1.0)), Is.EqualTo(1.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(1.000001)), Is.EqualTo(1.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(1.999999)), Is.EqualTo(1.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(2.0)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(2.000001)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(2.5)), Is.EqualTo(0.0f));
        }
Exemple #15
0
        private AnimationClip[] ExtractAnimations(
            AnimationContentDictionary animationDictionary,
            List <string> boneNameList,
            ContentProcessorContext context)
        {
            if (animationDictionary.Count < 1)
            {
                context.Logger.LogImportantMessage("Warning: No animations found.");
            }

            AnimationClip[] animations = new AnimationClip[animationDictionary.Count];

            int animationCount = 0;

            foreach (AnimationContent animationContent in animationDictionary.Values)
            {
                List <Keyframe> keyframes = new List <Keyframe>();

                // Each bone has its own channel
                foreach (string bone in animationContent.Channels.Keys)
                {
                    AnimationChannel animationChannel = animationContent.Channels[bone];
                    int boneIndex = boneNameList.IndexOf(bone);

                    foreach (AnimationKeyframe keyframe in animationChannel)
                    {
                        keyframes.Add(new Keyframe(
                                          keyframe.Time, boneIndex, keyframe.Transform));
                    }
                }

                // Sort all animation frames by time
                keyframes.Sort();

                animations[animationCount++] = new AnimationClip(
                    animationContent.Name,
                    animationContent.Duration,
                    keyframes.ToArray());
            }

            return(animations);
        }
        internal static AnimationClip Read(ContentReader input)
        {
            string animationName = input.ReadString();
            TimeSpan animationDuration = input.ReadObject<TimeSpan>();

            // Read animation clip channels
            Dictionary<string, AnimationChannel> animationChannelDictionary =
                new Dictionary<string, AnimationChannel>();

            int numAnimationChannels = input.ReadInt32();
            for (int i = 0; i < numAnimationChannels; i++)
            {
                string channelName = input.ReadString();

                // Read animation channel keyframes
                int numChannelKeyframes = input.ReadInt32();
                List<AnimationChannelKeyframe> keyframeList =
                    new List<AnimationChannelKeyframe>(numChannelKeyframes);

                for (int j = 0; j < numChannelKeyframes; j++)
                {
                    TimeSpan keyframeTime = input.ReadObject<TimeSpan>();

                    // Read keyframe pose
                    Pose keyframePose;
                    keyframePose.Translation = input.ReadVector3();
                    keyframePose.Orientation = input.ReadQuaternion();
                    keyframePose.Scale = input.ReadVector3();

                    keyframeList.Add(new AnimationChannelKeyframe(keyframeTime, keyframePose));
                }

                AnimationChannel animationChannel = new AnimationChannel(keyframeList);

                // Add the animation channel to the dictionary
                animationChannelDictionary.Add(channelName, animationChannel);
            }

            return
                new AnimationClip(animationName, animationDuration,
                    new AnimationChannelDictionary(animationChannelDictionary));
        }
Exemple #17
0
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);
        var indent = EditorGUI.indentLevel;

        EditorGUI.indentLevel = 0;
        var targetObjects    = property.serializedObject.targetObjects;
        var target           = property.serializedObject.targetObject;
        var field            = GetField(target.GetType(), property.name);
        AnimationChannel val = (AnimationChannel)field.GetValue(target);

        val = (AnimationChannel)EditorGUI.EnumMaskField(position, "Animation channels", val);
        for (int i = 0; i < targetObjects.Length; i++)
        {
            target = targetObjects[i];
            field.SetValue(target, val);
        }
        EditorGUI.indentLevel = indent;
        EditorGUI.EndProperty();
    }
Exemple #18
0
        private AnimationData[] ExtractAnimations(AnimationContentDictionary animationDictionary, List <string> boneNameList,
                                                  ContentProcessorContext context)
        {
            context.Logger.LogImportantMessage("{0} animations found.", animationDictionary.Count);
            AnimationData[] animations = new AnimationData[animationDictionary.Count];

            int count = 0;

            foreach (AnimationContent animationContent in animationDictionary.Values)
            {
                // Store all keyframes of the animation
                List <Keyframe> keyframes = new List <Keyframe>();

                // Go through all animation channels (Each bone has it's own channel)
                foreach (string animationKey in animationContent.Channels.Keys)
                {
                    AnimationChannel animationChannel = animationContent.Channels[animationKey];
                    int boneIndex = boneNameList.IndexOf(animationKey);

                    //context.Logger.LogImportantMessage("{0} - Bone: ", animationKey, boneIndex);
                    foreach (AnimationKeyframe keyframe in animationChannel)
                    {
                        keyframes.Add(new Keyframe(keyframe.Time, boneIndex, keyframe.Transform));
                    }
                }

                context.Logger.LogImportantMessage("Animation {0}: {1} channels found, {2} keyframes found.",
                                                   animationContent.Name, animationContent.Channels.Count, keyframes.Count);

                // Sort all animation frames by time
                keyframes.Sort();

                animations[count++] = new
                                      AnimationData(animationContent.Name, animationContent.Duration, keyframes.ToArray());
            }

            return(animations);
        }
        public void TestDiscontinuity()
        {
            var animationChannel = new AnimationChannel();
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.Zero, Value = 0.0f });
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.FromSeconds(1.0), Value = 0.0f });
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.FromSeconds(1.0), Value = 0.0f });
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.FromSeconds(1.0), Value = 1.0f });
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.FromSeconds(1.0), Value = 1.0f });
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.FromSeconds(2.0), Value = 1.0f });
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.FromSeconds(2.0), Value = 1.0f });
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.FromSeconds(2.0), Value = 0.0f });
            animationChannel.KeyFrames.Add(new KeyFrameData<float> { Time = CompressedTimeSpan.FromSeconds(2.0), Value = 0.0f });

            var evaluator = new AnimationChannel.Evaluator(animationChannel.KeyFrames);
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(0.0)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(0.999999)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(1.0)), Is.EqualTo(1.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(1.000001)), Is.EqualTo(1.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(1.999999)), Is.EqualTo(1.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(2.0)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(2.000001)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(2.5)), Is.EqualTo(0.0f));
        }
        List <AnimationContent> CreateAnimations()
        {
            var animations = new List <AnimationContent>();

            for (int i = 0; i < collada.JointAnimations.Count; i++)
            {
                var sourceAnim = collada.JointAnimations[i];

                AnimationContent animation = new AnimationContent();
                animation.Name     = sourceAnim.Name ?? String.Format("Take {0:000}", (i + 1));
                animation.Duration = TimeSpan.FromSeconds(sourceAnim.EndTime - sourceAnim.StartTime);

                foreach (var sourceChannel in sourceAnim.Channels)
                {
                    AnimationChannel channel = new AnimationChannel();

                    // Adds the different keyframes to the animation channel
                    // NOTE: Might be better to sample the keyframes
                    foreach (var sourceKeyframe in sourceChannel.Sampler.Keyframes)
                    {
                        TimeSpan time      = TimeSpan.FromSeconds(sourceKeyframe.Time);
                        Matrix   transform = sourceKeyframe.Transform;

                        AnimationKeyframe keyframe = new AnimationKeyframe(time, transform);
                        channel.Add(keyframe);
                    }

                    String key = GetJointKey(sourceChannel.Target);
                    animation.Channels.Add(key, channel);
                }

                animation.OpaqueData.Add("FPS", sourceAnim.FramesPerSecond);
                animations.Add(animation);
            }

            return(animations);
        }
Exemple #21
0
        /// <summary>
        /// SampleChannel will be called to sample the transformation of a given channel
        /// at a given time. The given index parameter is for use by the sample function,
        /// to avoid having to look for the "base" frame each call. It will start out as
        /// zero in the first call for a given channel. "time" will be monotonically
        /// increasing for a given channel.
        /// </summary>
        /// <param name="achan">The channel to sample from.</param>
        /// <param name="time">The time to sample at (monotonically increasing).</param>
        /// <param name="ix">For use by SampleChannel (starts at 0 for each new channel).</param>
        /// <returns>The sampled keyframe output (allocated by this function).</returns>
        protected virtual Keyframe SampleChannel(AnimationChannel achan, float time, ref int ix)
        {
            Keyframe          ret   = new Keyframe();
            AnimationKeyframe akf0  = achan[ix];
            float             scale = CalcTransformScale(akf0.Transform);
            //todo:  really should be done in world space, but I'm giving up now
            float offset = akf0.Transform.Translation.Length();

            if (scale > maxScale_)
            {
                maxScale_ = scale;
            }
            if (offset > maxOffset_)
            {
                maxOffset_ = offset;
            }
again:
            if (ix == achan.Count - 1)
            {
                return(KeyframeFromMatrix(akf0.Transform, ret));
            }
            AnimationKeyframe akf1 = achan[ix + 1];

            if (akf1.Time.TotalSeconds <= time)
            {
                akf0 = akf1;
                ++ix;
                goto again;
            }
            KeyframeFromMatrix(akf0.Transform, tmpA_);
            KeyframeFromMatrix(akf1.Transform, tmpB_);
            Keyframe.Interpolate(tmpA_, tmpB_,
                                 (float)((time - akf0.Time.TotalSeconds) / (akf1.Time.TotalSeconds - akf0.Time.TotalSeconds)),
                                 ret);
            return(ret);
        }
Exemple #22
0
        /*
         * template Animation
         * {
         * [...]
         * }
         */
        /// <summary>
        /// Fills in all the channels of an animation.  Each channel refers to
        /// a single bone's role in the animation.
        /// </summary>
        /// <param name="boneName">The name of the bone associated with the channel</param>
        /// <returns>The imported animation channel</returns>
        private AnimationChannel ImportAnimationChannel(out string boneName)
        {
            AnimationChannel anim = new AnimationChannel();

            // Store the frames in an array, which acts as an intermediate data set
            // This will allow us to more easily provide support for non Matrix
            // animation keys at a later time
            AnimationKeyframe[]      rotFrames    = null;
            AnimationKeyframe[]      transFrames  = null;
            AnimationKeyframe[]      scaleFrames  = null;
            List <AnimationKeyframe> matrixFrames = null;

            boneName = null;
            tokens.SkipName();
            for (string next = tokens.NextToken(); next != "}"; next = tokens.NextToken())
            {
                // A set of animation keys
                if (next == "AnimationKey")
                {
                    // These keys can be rotation (0),scale(1),translation(2), or matrix(3 or 4) keys.
                    int keyType;
                    AnimationKeyframe[] frames = ImportAnimationKey(out keyType);
                    if (keyType == 0)
                    {
                        rotFrames = frames;
                    }
                    else if (keyType == 1)
                    {
                        scaleFrames = frames;
                    }
                    else if (keyType == 2)
                    {
                        transFrames = frames;
                    }
                    else
                    {
                        matrixFrames = new List <AnimationKeyframe>(frames);
                    }
                }
                // A possible bone name
                else if (next == "{")
                {
                    string token = tokens.NextToken();
                    if (tokens.NextToken() != "}")
                    {
                        tokens.SkipNode();
                    }
                    else
                    {
                        boneName = token;
                    }
                }
            }
            // Fill in the channel with the frames
            if (matrixFrames != null)
            {
                matrixFrames.Sort(new Comparison <AnimationKeyframe>(delegate(AnimationKeyframe one,
                                                                              AnimationKeyframe two)
                {
                    return(one.Time.CompareTo(two.Time));
                }));
                if (matrixFrames[0].Time != TimeSpan.Zero)
                {
                    matrixFrames.Insert(0, new AnimationKeyframe(new TimeSpan(),
                                                                 matrixFrames[0].Transform));
                }
                for (int i = 0; i < matrixFrames.Count; i++)
                {
                    Matrix m = matrixFrames[i].Transform;
                    ContentUtil.ReflectMatrix(ref m);
                    matrixFrames[i].Transform = m;
                    anim.Add(matrixFrames[i]);
                }
            }
            else
            {
                List <AnimationKeyframe> combinedFrames = ContentUtil.MergeKeyFrames(
                    scaleFrames, transFrames, rotFrames);
                for (int i = 0; i < combinedFrames.Count; i++)
                {
                    Matrix m = combinedFrames[i].Transform;
                    ContentUtil.ReflectMatrix(ref m);
                    combinedFrames[i].Transform = m; //* Matrix.CreateRotationX(MathHelper.PiOver2);
                    anim.Add(combinedFrames[i]);
                }
            }
            return(anim);
        }
        public static void Split(AnimationContentDictionary animationDictionary, IList <AnimationSplitDefinition> splits, ContentIdentity contentIdentity, ContentProcessorContext context)
        {
            if (splits == null || splits.Count == 0)
            {
                return;
            }

            if (animationDictionary == null)
            {
                return;
            }

            if (contentIdentity == null)
            {
                throw new ArgumentNullException("contentIdentity");
            }

            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (animationDictionary.Count == 0)
            {
                context.Logger.LogWarning(null, contentIdentity, "The model does not have an animation. Animation splitting is skipped.");
                return;
            }

            if (animationDictionary.Count > 1)
            {
                context.Logger.LogWarning(null, contentIdentity, "The model contains more than 1 animation. The animation splitting is performed on the first animation. Other animations are deleted!");
            }

            // Get first animation.
            var originalAnimation = animationDictionary.First().Value;

            // Clear animation dictionary. - We do not keep the original animations!
            animationDictionary.Clear();

            // Add an animation to animationDictionary for each split.
            foreach (var split in splits)
            {
                TimeSpan startTime = split.StartTime;
                TimeSpan endTime   = split.EndTime;

                var newAnimation = new AnimationContent
                {
                    Name     = split.Name,
                    Duration = endTime - startTime
                };

                // Process all channels.
                foreach (var item in originalAnimation.Channels)
                {
                    string           channelName     = item.Key;
                    AnimationChannel originalChannel = item.Value;
                    if (originalChannel.Count == 0)
                    {
                        return;
                    }

                    AnimationChannel newChannel = new AnimationChannel();

                    // Add all key frames to the channel that are in the split interval.
                    foreach (AnimationKeyframe keyFrame in originalChannel)
                    {
                        TimeSpan time = keyFrame.Time;
                        if (startTime <= time && time <= endTime)
                        {
                            newChannel.Add(new AnimationKeyframe(keyFrame.Time - startTime, keyFrame.Transform));
                        }
                    }

                    // Add channel if it contains key frames.
                    if (newChannel.Count > 0)
                    {
                        newAnimation.Channels.Add(channelName, newChannel);
                    }
                }

                if (newAnimation.Channels.Count == 0)
                {
                    var message = string.Format(CultureInfo.InvariantCulture, "The split animation '{0}' is empty.", split.Name);
                    throw new InvalidContentException(message, contentIdentity);
                }

                if (animationDictionary.ContainsKey(split.Name))
                {
                    var message = string.Format(CultureInfo.InvariantCulture, "Cannot add split animation '{0}' because an animation with the same name already exits.", split.Name);
                    throw new InvalidContentException(message, contentIdentity);
                }

                animationDictionary.Add(split.Name, newAnimation);
            }
        }
Exemple #24
0
        public Animation_SamplerType(List <string> imageList)
        {
            var baseColorTexture = new Texture {
                Source = UseTexture(imageList, "BaseColor_Cube")
            };

            CommonProperties.Add(new Property(PropertyName.Target, "Rotation"));
            CommonProperties.Add(new Property(PropertyName.Interpolation, "Linear"));

            Model CreateModel(DataType outputType)
            {
                var properties        = new List <Property>();
                var cubeMeshPrimitive = MeshPrimitive.CreateCube();

                // Apply the common properties to the gltf.
                cubeMeshPrimitive.Material = new Runtime.Material
                {
                    PbrMetallicRoughness = new PbrMetallicRoughness
                    {
                        BaseColorTexture = new TextureInfo {
                            Texture = baseColorTexture
                        },
                    },
                };
                var node = new Node
                {
                    Mesh = new Runtime.Mesh
                    {
                        MeshPrimitives = new[]
                        {
                            cubeMeshPrimitive
                        }
                    }
                };
                var channel = new AnimationChannel
                {
                    Target = new AnimationChannelTarget
                    {
                        Node = node,
                        Path = AnimationChannelTargetPath.Rotation,
                    },
                    Sampler = new AnimationSampler
                    {
                        Interpolation = AnimationSamplerInterpolation.Linear,
                        Input         = Data.Create(new[]
                        {
                            0.0f,
                            1.0f,
                            2.0f,
                            3.0f,
                            4.0f,
                        }),
                        Output = Data.Create(new[]
                        {
                            Quaternion.CreateFromYawPitchRoll(FloatMath.ToRadians(90.0f), 0.0f, 0.0f),
                            Quaternion.Identity,
                            Quaternion.CreateFromYawPitchRoll(FloatMath.ToRadians(-90.0f), 0.0f, 0.0f),
                            Quaternion.Identity,
                            Quaternion.CreateFromYawPitchRoll(FloatMath.ToRadians(90.0f), 0.0f, 0.0f),
                        }, outputType),
                    },
                };

                // Apply the properties that are specific to this gltf.
                properties.Add(new Property(PropertyName.SamplerOutputComponentType, outputType.ToReadmeString()));

                // Create the gltf object.
                GLTF gltf = CreateGLTF(() => new Scene
                {
                    Nodes = new[]
                    {
                        node
                    },
                });

                gltf.Animations = new[]
                {
                    new Animation
                    {
                        Channels = new List <AnimationChannel>
                        {
                            channel
                        }
                    }
                };
                return(new Model
                {
                    Properties = properties,
                    GLTF = gltf,
                    Animated = true,
                });
            }

            Models = new List <Model>
            {
                CreateModel(DataType.Float),
                CreateModel(DataType.NormalizedByte),
                CreateModel(DataType.NormalizedShort),
            };

            GenerateUsedPropertiesList();
        }
        public static void Split(AnimationContentDictionary animationDictionary, IList<AnimationSplitDefinition> splits, ContentIdentity contentIdentity, ContentProcessorContext context)
        {
            if (splits == null || splits.Count == 0)
            return;

              if (animationDictionary == null)
            return;

              if (contentIdentity == null)
            throw new ArgumentNullException("contentIdentity");

              if (context == null)
            throw new ArgumentNullException("context");

              if (animationDictionary.Count == 0)
              {
            context.Logger.LogWarning(null, contentIdentity, "The model does not have an animation. Animation splitting is skipped.");
            return;
              }

              if (animationDictionary.Count > 1)
            context.Logger.LogWarning(null, contentIdentity, "The model contains more than 1 animation. The animation splitting is performed on the first animation. Other animations are deleted!");

              // Get first animation.
              var originalAnimation = animationDictionary.First().Value;

              // Clear animation dictionary. - We do not keep the original animations!
              animationDictionary.Clear();

              // Add an animation to animationDictionary for each split.
              foreach (var split in splits)
              {
            TimeSpan startTime = split.StartTime;
            TimeSpan endTime = split.EndTime;

            var newAnimation = new AnimationContent
            {
              Name = split.Name,
              Duration = endTime - startTime
            };

            // Process all channels.
            foreach (var item in originalAnimation.Channels)
            {
              string channelName = item.Key;
              AnimationChannel originalChannel = item.Value;
              if (originalChannel.Count == 0)
            return;

              AnimationChannel newChannel = new AnimationChannel();

              // Add all key frames to the channel that are in the split interval.
              foreach (AnimationKeyframe keyFrame in originalChannel)
              {
            TimeSpan time = keyFrame.Time;
            if (startTime <= time && time <= endTime)
            {
              newChannel.Add(new AnimationKeyframe(keyFrame.Time - startTime, keyFrame.Transform));
            }
              }

              // Add channel if it contains key frames.
              if (newChannel.Count > 0)
            newAnimation.Channels.Add(channelName, newChannel);
            }

            if (newAnimation.Channels.Count == 0)
            {
              var message = string.Format(CultureInfo.InvariantCulture, "The split animation '{0}' is empty.", split.Name);
              throw new InvalidContentException(message, contentIdentity);
            }

            if (animationDictionary.ContainsKey(split.Name))
            {
              var message = string.Format(CultureInfo.InvariantCulture, "Cannot add split animation '{0}' because an animation with the same name already exits.", split.Name);
              throw new InvalidContentException(message, contentIdentity);
            }

            animationDictionary.Add(split.Name, newAnimation);
              }
        }
 /// <summary>
 /// SampleChannel will be called to sample the transformation of a given channel 
 /// at a given Clock. The given index parameter is for use by the sample function, 
 /// to avoid having to look for the "base" frame each call. It will start out as 
 /// zero in the first call for a given channel. "Clock" will be monotonically 
 /// increasing for a given channel.
 /// </summary>
 /// <param name="achan">The channel to sample from.</param>
 /// <param name="Clock">The Clock to sample at (monotonically increasing).</param>
 /// <param name="ix">For use by SampleChannel (starts at 0 for each new channel).</param>
 /// <returns>The sampled keyframe output (allocated by this function).</returns>
 protected virtual Keyframe SampleChannel(AnimationChannel achan, float time, ref int ix)
 {
     Keyframe ret = new Keyframe();
       AnimationKeyframe akf0 = achan[ix];
       float scale = CalcTransformScale(akf0.Transform);
       //todo:  really should be done in world space, but I'm giving up now
       float offset = akf0.Transform.Translation.Length();
       if (scale > maxScale_)
     maxScale_ = scale;
       if (offset > maxOffset_)
     maxOffset_ = offset;
     again:
       if (ix == achan.Count - 1)
     return KeyframeFromMatrix(akf0.Transform, ret);
       AnimationKeyframe akf1 = achan[ix+1];
       if (akf1.Time.TotalSeconds <= time)
       {
     akf0 = akf1;
     ++ix;
     goto again;
       }
       KeyframeFromMatrix(akf0.Transform, tmpA_);
       KeyframeFromMatrix(akf1.Transform, tmpB_);
       Keyframe.Interpolate(tmpA_, tmpB_,
       (float)((time - akf0.Time.TotalSeconds) / (akf1.Time.TotalSeconds - akf0.Time.TotalSeconds)),
       ret);
       return ret;
 }
Exemple #27
0
        /// <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);
        }
        /// <summary>
        /// Retrieves and interpolates the pose of an animation channel.
        /// </summary>
        /// <param name="animationChannel">Name of the animation channel.</param>
        /// <param name="animationTime">Current animation clip time.</param>
        /// <param name="outPose">The output interpolated pose.</param>
        private void InterpolateChannelPose(AnimationChannel animationChannel, TimeSpan animationTime,
            out Pose outPose)
        {
            if (translationInterpolation == InterpolationMode.None &&
                orientationInterpolation == InterpolationMode.None &&
                    scaleInterpolation == InterpolationMode.None)
            {
                int keyframeIndex = animationChannel.GetKeyframeIndexByTime(animationTime);
                outPose = animationChannel[keyframeIndex].Pose;
            }
            else
            {
                int keyframeIndex = animationChannel.GetKeyframeIndexByTime(animationTime);
                int nextKeyframeIndex;

                // If we are looping then the next frame may wrap around to
                // the beginning. If not we should just clamp it at the last frame
                if (loopEnabled)
                {
                    nextKeyframeIndex = (keyframeIndex + 1) % animationChannel.Count;
                }
                else
                {
                    nextKeyframeIndex = Math.Min(keyframeIndex + 1, animationChannel.Count - 1);
                }

                AnimationChannelKeyframe keyframe1 = animationChannel[keyframeIndex];
                AnimationChannelKeyframe keyframe2 = animationChannel[nextKeyframeIndex];

                // Calculate the time between the keyframes considering loop
                long keyframeDuration;
                if (keyframeIndex == (animationChannel.Count - 1))
                    keyframeDuration = animationClip.Duration.Ticks - keyframe1.Time.Ticks;
                else
                    keyframeDuration = keyframe2.Time.Ticks - keyframe1.Time.Ticks;

                // Interpolate when duration higher than zero
                if (keyframeDuration > 0)
                {
                    long elapsedKeyframeTime = animationTime.Ticks - keyframe1.Time.Ticks;
                    float lerpFactor = elapsedKeyframeTime / (float)keyframeDuration;

                    outPose =
                        Pose.Interpolate(keyframe1.Pose, keyframe2.Pose, lerpFactor,
                            translationInterpolation, orientationInterpolation, scaleInterpolation);
                }
                // Otherwise don't interpolate
                else
                    outPose = keyframe1.Pose;
            }
        }
		internal override void Reset(bool newUsage, bool keepStored)
		{
			base.Reset(newUsage, keepStored);

			readFrameIndex = -1;
			currentFrameTime = 0;
			previousFrameTime = 0;

			if (channels != null)
			{
				for (int i = 0; i < channels.Length; i++)
				{
					if (!keepStored)
					{
						channels[i] = new AnimationChannel();
						channels[i].storeTransform = Transform.Identity;
					}
					channels[i].frameReader = new CompressedTransformReader();
					channels[i].lerpedTransform = Transform.Identity;
					channels[i].sourceData = animation.GetBoneCompressedTransformData(i);
					channels[i].sourceTransformData = animation.GetBoneDecompressedTransformData(i);
					channels[i].boneIndex = animation.BoneIndices[i];
					channels[i].readIndex = 0;
				}
			}
		}
        /// <summary>
        /// Called when an XML document is read that specifies how animations
        /// should be split.
        /// </summary>
        /// <param name="animDict">The dictionary of animation name/AnimationContent
        /// pairs. </param>
        /// <param name="doc">The Xml document that contains info on how to split
        /// the animations.</param>
        protected virtual void SubdivideAnimations(
            AnimationContentDictionary animDict, XmlDocument doc)
        {
            string[] animNames = new string[animDict.Keys.Count];
            animDict.Keys.CopyTo(animNames, 0);
            if (animNames.Length == 0)
            {
                return;
            }

            // Traverse each xml node that represents an animation to be subdivided
            foreach (XmlNode node in doc)
            {
                XmlElement child = node as XmlElement;
                if (child == null || child.Name != "animation")
                {
                    continue;
                }

                string animName = null;
                if (child["name"] != null)
                {
                    // The name of the animation to be split
                    animName = child["name"].InnerText;
                }
                else if (child["index"] != null)
                {
                    animName = animNames[int.Parse(child["index"].InnerText)];
                }
                else
                {
                    animName = animNames[0];
                }

                // If the tickspersecond node is filled, use that to calculate seconds per tick
                double animTicksPerSecond = 1.0, secondsPerTick = 0;
                try
                {
                    if (child["tickspersecond"] != null)
                    {
                        animTicksPerSecond = double.Parse(child["tickspersecond"].InnerText);
                    }
                }
                catch
                {
                    throw new Exception("Error parsing tickspersecond in xml file.");
                }
                if (animTicksPerSecond <= 0)
                {
                    throw new InvalidDataException("AnimTicksPerSecond in XML file must be " +
                                                   "a positive number.");
                }
                secondsPerTick = 1.0 / animTicksPerSecond;

                AnimationContent anim = null;
                // Get the animation and remove it from the dict
                // Check to see if the animation specified in the xml file exists
                try
                {
                    anim = animDict[animName];
                }
                catch
                {
                    throw new Exception("Animation named " + animName + " specified in XML file does not exist in model.");
                }
                animDict.Remove(anim.Name);
                // Get the list of new animations
                XmlNodeList subAnimations = child.GetElementsByTagName("animationsubset");

                foreach (XmlElement subAnim in subAnimations)
                {
                    // Create the new sub animation
                    AnimationContent newAnim            = new AnimationContent();
                    XmlElement       subAnimNameElement = subAnim["name"];

                    if (subAnimNameElement != null)
                    {
                        newAnim.Name = subAnimNameElement.InnerText;
                    }

                    // If a starttime node exists, use that to get the start time
                    long startTime, endTime;
                    if (subAnim["starttime"] != null)
                    {
                        try
                        {
                            startTime = TimeSpan.FromSeconds(double.Parse(subAnim["starttime"].InnerText)).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing starttime node in XML file.  Node inner text "
                                                + "must be a non negative number.");
                        }
                    }
                    else if (subAnim["startframe"] != null)// else use the secondspertick combined with the startframe node value
                    {
                        try
                        {
                            double seconds =
                                double.Parse(subAnim["startframe"].InnerText) * secondsPerTick;

                            startTime = TimeSpan.FromSeconds(
                                seconds).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing startframe node in XML file.  Node inner text "
                                                + "must be a non negative number.");
                        }
                    }
                    else
                    {
                        throw new Exception("Sub animation in XML file must have either a starttime or startframe node.");
                    }

                    // Same with endtime/endframe
                    if (subAnim["endtime"] != null)
                    {
                        try
                        {
                            endTime = TimeSpan.FromSeconds(double.Parse(subAnim["endtime"].InnerText)).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing endtime node in XML file.  Node inner text "
                                                + "must be a non negative number.");
                        }
                    }
                    else if (subAnim["endframe"] != null)
                    {
                        try
                        {
                            double seconds = double.Parse(subAnim["endframe"].InnerText)
                                             * secondsPerTick;
                            endTime = TimeSpan.FromSeconds(
                                seconds).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing endframe node in XML file.  Node inner text "
                                                + "must be a non negative number.");
                        }
                    }
                    else
                    {
                        throw new Exception("Sub animation in XML file must have either an endtime or endframe node.");
                    }

                    if (endTime < startTime)
                    {
                        throw new Exception("Start time must be <= end time in XML file.");
                    }

                    // Now that we have the start and end times, we associate them with
                    // start and end indices for each animation track/channel
                    foreach (KeyValuePair <string, AnimationChannel> k in anim.Channels)
                    {
                        // The current difference between the start time and the
                        // time at the current index
                        long currentStartDiff;
                        // The current difference between the end time and the
                        // time at the current index
                        long currentEndDiff;
                        // The difference between the start time and the time
                        // at the start index
                        long bestStartDiff = long.MaxValue;
                        // The difference between the end time and the time at
                        // the end index
                        long bestEndDiff = long.MaxValue;

                        // The start and end indices
                        int startIndex = -1;
                        int endIndex   = -1;

                        // Create a new channel and reference the old channel
                        AnimationChannel newChan = new AnimationChannel();
                        AnimationChannel oldChan = k.Value;

                        // Iterate through the keyframes in the channel
                        for (int i = 0; i < oldChan.Count; i++)
                        {
                            // Update the startIndex, endIndex, bestStartDiff,
                            // and bestEndDiff
                            long ticks = oldChan[i].Time.Ticks;
                            currentStartDiff = Math.Abs(startTime - ticks);
                            currentEndDiff   = Math.Abs(endTime - ticks);
                            if (startIndex == -1 || currentStartDiff < bestStartDiff)
                            {
                                startIndex    = i;
                                bestStartDiff = currentStartDiff;
                            }
                            if (endIndex == -1 || currentEndDiff < bestEndDiff)
                            {
                                endIndex    = i;
                                bestEndDiff = currentEndDiff;
                            }
                        }


                        // Now we have our start and end index for the channel
                        for (int i = startIndex; i <= endIndex; i++)
                        {
                            AnimationKeyframe frame = oldChan[i];
                            long time;
                            // Clamp the time so that it can't be less than the
                            // start time
                            if (frame.Time.Ticks < startTime)
                            {
                                time = 0;
                            }
                            // Clamp the time so that it can't be greater than the
                            // end time
                            else if (frame.Time.Ticks > endTime)
                            {
                                time = endTime - startTime;
                            }
                            else // Else get the time
                            {
                                time = frame.Time.Ticks - startTime;
                            }

                            // Finally... create the new keyframe and add it to the new channel
                            AnimationKeyframe keyframe = new AnimationKeyframe(
                                TimeSpan.FromTicks(time),
                                frame.Transform);

                            newChan.Add(keyframe);
                        }

                        // Add the channel and update the animation duration based on the
                        // length of the animation track.
                        newAnim.Channels.Add(k.Key, newChan);
                        if (newChan[newChan.Count - 1].Time > newAnim.Duration)
                        {
                            newAnim.Duration = newChan[newChan.Count - 1].Time;
                        }
                    }
                    try
                    {
                        // Add the subdived animation to the dictionary.
                        animDict.Add(newAnim.Name, newAnim);
                    }
                    catch
                    {
                        throw new Exception("Attempt to add an animation when one by the same name already exists. " +
                                            "Name: " + newAnim.Name);
                    }
                }
            }
        }
        private void _exportAnimation(UnityEngine.AnimationClip animationClip)
        {
            //
            var frameCount    = (int)Math.Floor(animationClip.length * animationClip.frameRate) + 1;
            var curveBinds    = UnityEditor.AnimationUtility.GetCurveBindings(animationClip);
            var ignoreCurves  = new List <UnityEditor.EditorCurveBinding>();
            var glTFAnimation = new GLTF.Schema.Animation()
            {
                Name       = animationClip.name,
                Channels   = new List <AnimationChannel>(),
                Samplers   = new List <AnimationSampler>(),
                Extensions = new Dictionary <string, IExtension>()
                {
                    {
                        AnimationExtensionFactory.EXTENSION_NAME,
                        new AnimationExtension()
                        {
                            frameRate = animationClip.frameRate,
                            clips     = new List <AnimationClip>()
                            {
                                new AnimationClip()
                                {
                                    name      = animationClip.name,
                                    playTimes = _getPlayTimes(animationClip),
                                    position  = 0.0f,
                                    duration  = (float)Math.Round(animationClip.length, 6),
                                }
                            }
                        }
                    },
                },
            };
            var ext = glTFAnimation.Extensions[AnimationExtensionFactory.EXTENSION_NAME] as AnimationExtension;

            this._root.Animations.Add(glTFAnimation);
            // Input.
            var inputAccessor = new Accessor();

            inputAccessor.Count         = frameCount;
            inputAccessor.Type          = GLTFAccessorAttributeType.SCALAR;
            inputAccessor.ComponentType = GLTFComponentType.Float;
            inputAccessor.BufferView    = new BufferViewId
            {
                Id   = 0,
                Root = _root
            };
            this._root.Accessors.Add(inputAccessor);

            // Write input.
            for (var i = 0; i < frameCount; ++i)
            {
                //_bufferWriter.Write(Math.Round(Math.Min(animationClip.length * i / (frameCount - 1), animationClip.length), 6)); // TODO
                _bufferWriter.Write(i / animationClip.frameRate);
            }

            var MainTex_STy = new List <float>();

            foreach (var curveBind in curveBinds)
            {
                // Curve has been parsed.
                if (ignoreCurves.Contains(curveBind))
                {
                    continue;
                }
                // No target.
                var animationTarget = _target.Find(curveBind.path);
                if (animationTarget == null)
                {
                    continue;
                }
                // Create node.
                var nodeIndex = _animationTargets.IndexOf(animationTarget);
                if (nodeIndex < 0)
                {
                    _animationTargets.Add(animationTarget);
                    nodeIndex = _root.Nodes.Count;
                    _root.Nodes.Add(new Node()
                    {
                        Name = _target == animationTarget ? "__root__" : animationTarget.name,
                    });

                    if (animationTarget.transform.parent == _target)
                    {
                        _root.Scenes[0].Nodes.Add(
                            new NodeId()
                        {
                            Id   = nodeIndex,
                            Root = _root,
                        }
                            );
                    }
                }
                // Output.
                var outputAccessor = new Accessor();
                outputAccessor.Count         = frameCount;
                outputAccessor.ComponentType = GLTFComponentType.Float;
                outputAccessor.BufferView    = inputAccessor.BufferView;
                outputAccessor.ByteOffset    = (int)_bufferWriter.BaseStream.Position;
                this._root.Accessors.Add(outputAccessor);
                //
                var animationSampler = new AnimationSampler()
                {
                    Input = new AccessorId()
                    {
                        Id   = this._root.Accessors.IndexOf(inputAccessor),
                        Root = _root,
                    },
                    Interpolation = InterpolationType.LINEAR,
                    Output        = new AccessorId()
                    {
                        Id   = this._root.Accessors.IndexOf(outputAccessor),
                        Root = _root,
                    },
                };
                glTFAnimation.Samplers.Add(animationSampler);
                //
                var animationChannel = new AnimationChannel()
                {
                    Sampler = new SamplerId()
                    {
                        Id   = glTFAnimation.Samplers.IndexOf(animationSampler),
                        Root = _root,
                    },
                    Target = new AnimationChannelTarget()
                    {
                        Node = new NodeId()
                        {
                            Id   = nodeIndex,
                            Root = _root,
                        }
                    }
                };
                glTFAnimation.Channels.Add(animationChannel);

                if (curveBind.type == typeof(Transform))
                {
                    var curveGroup = _getCurveGroup(curveBinds, curveBind);
                    ignoreCurves.AddRange(curveGroup);

                    switch (curveBind.propertyName)
                    {
                    case "m_LocalPosition.x":
                    case "m_LocalPosition.y":
                    case "m_LocalPosition.z":
                        animationChannel.Target.Path = GLTFAnimationChannelPath.translation;
                        outputAccessor.Type          = GLTFAccessorAttributeType.VEC3;

                        for (var i = 0; i < frameCount; ++i)
                        {
                            var time   = i / animationClip.frameRate;
                            var curveX = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[0]);
                            var curveY = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[1]);
                            var curveZ = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[2]);
                            var value  = curveX != null?curveX.Evaluate(time) : animationTarget.transform.localPosition.x;

                            _bufferWriter.Write(value);
                            value = curveY != null?curveY.Evaluate(time) : animationTarget.transform.localPosition.y;

                            _bufferWriter.Write(value);
                            value = curveZ != null?curveZ.Evaluate(time) : animationTarget.transform.localPosition.z;

                            _bufferWriter.Write(value);
                        }
                        break;

                    case "m_LocalRotation.x":
                    case "m_LocalRotation.y":
                    case "m_LocalRotation.z":
                    case "m_LocalRotation.w":
                        animationChannel.Target.Path = GLTFAnimationChannelPath.rotation;
                        outputAccessor.Type          = GLTFAccessorAttributeType.VEC4;

                        for (var i = 0; i < frameCount; ++i)
                        {
                            var time   = i / animationClip.frameRate;
                            var curveX = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[0]);
                            var curveY = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[1]);
                            var curveZ = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[2]);
                            var curveW = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[3]);
                            var valueX = curveX != null?curveX.Evaluate(time) : animationTarget.transform.localRotation.x;

                            var valueY = curveY != null?curveY.Evaluate(time) : animationTarget.transform.localRotation.y;

                            var valueZ = curveZ != null?curveZ.Evaluate(time) : animationTarget.transform.localRotation.z;

                            var valueW = curveW != null?curveW.Evaluate(time) : animationTarget.transform.localRotation.w;

                            _bufferWriter.Write(valueX);
                            _bufferWriter.Write(valueY);
                            _bufferWriter.Write(valueZ);
                            _bufferWriter.Write(valueW);
                        }
                        break;

                    case "localEulerAnglesRaw.x":
                    case "localEulerAnglesRaw.y":
                    case "localEulerAnglesRaw.z":
                        animationChannel.Target.Path = GLTFAnimationChannelPath.rotation;
                        outputAccessor.Type          = GLTFAccessorAttributeType.VEC4;

                        for (var i = 0; i < frameCount; ++i)
                        {
                            var time   = i / animationClip.frameRate;
                            var curveX = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[0]);
                            var curveY = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[1]);
                            var curveZ = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[2]);
                            var valueX = curveX != null?curveX.Evaluate(time) : animationTarget.transform.localEulerAngles.x;

                            var valueY = curveY != null?curveY.Evaluate(time) : animationTarget.transform.localEulerAngles.y;

                            var valueZ = curveZ != null?curveZ.Evaluate(time) : animationTarget.transform.localEulerAngles.z;

                            var quaternion = Quaternion.Euler(valueX, valueY, valueZ);

                            _bufferWriter.Write(quaternion.x);
                            _bufferWriter.Write(quaternion.y);
                            _bufferWriter.Write(quaternion.z);
                            _bufferWriter.Write(quaternion.w);
                        }
                        break;

                    case "m_LocalScale.x":
                    case "m_LocalScale.y":
                    case "m_LocalScale.z":
                        animationChannel.Target.Path = GLTFAnimationChannelPath.scale;
                        outputAccessor.Type          = GLTFAccessorAttributeType.VEC3;

                        for (var i = 0; i < frameCount; ++i)
                        {
                            var time   = i / animationClip.frameRate;
                            var curveX = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[0]);
                            var curveY = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[1]);
                            var curveZ = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[2]);
                            var value  = curveX != null?curveX.Evaluate(time) : animationTarget.transform.localScale.x;

                            _bufferWriter.Write(value);
                            value = curveY != null?curveY.Evaluate(time) : animationTarget.transform.localScale.y;

                            _bufferWriter.Write(value);
                            value = curveZ != null?curveZ.Evaluate(time) : animationTarget.transform.localScale.z;

                            _bufferWriter.Write(value);
                        }
                        break;
                    }
                }
                else
                {
                    animationChannel.Target.Path = GLTFAnimationChannelPath.custom;
                    outputAccessor.Type          = GLTFAccessorAttributeType.SCALAR;

                    var type       = "";
                    var property   = "";
                    var uri        = "";
                    var needUpdate = -1;

                    if (curveBind.type == typeof(GameObject))
                    {
                        type = "paper.GameObject";

                        switch (curveBind.propertyName)
                        {
                        case "m_IsActive":
                            property = "activeSelf";
                            animationSampler.Interpolation = InterpolationType.STEP;
                            break;
                        }

                        // for (var i = 0; i < frameCount; ++i) // TODO
                        // {
                        //     var time = animationClip.length * i / frameCountSO; // TODO
                        //     var curve = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveBind);
                        //     var value = curve.Evaluate(time);
                        //     _bufferWriter.Write(value);
                        // }
                    }
                    else if (curveBind.type == typeof(UnityEngine.MeshRenderer))
                    {
                        type       = "egret3d.MeshRenderer";
                        uri        = "materials/0/$/_uvTransform";
                        needUpdate = 1;
                        // animationSampler.Interpolation = InterpolationType.STEP;

                        switch (curveBind.propertyName)
                        {
                        case "material._MainTex_ST.z":
                            property = "0";
                            break;

                        case "material._MainTex_ST.w":
                            property = "1";
                            break;

                        case "material._MainTex_ST.x":
                            property = "2";
                            break;

                        case "material._MainTex_ST.y":
                            property = "3";
                            break;
                        }
                    }
                    else
                    {
                        Debug.Log("Unknown type and property." + curveBind.type.ToString() + curveBind.propertyName);
                    }

                    // Extensions.
                    animationChannel.Extensions = new Dictionary <string, IExtension>()
                    {
                        {
                            AnimationExtensionFactory.EXTENSION_NAME,
                            new AnimationChannelExtension()
                            {
                                type       = type,
                                property   = property,
                                uri        = uri,
                                needUpdate = needUpdate,
                            }
                        },
                    };

                    for (var i = 0; i < frameCount; ++i)
                    {
                        var curve = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveBind);
                        if (curve != null)
                        {
                            var value = curve.Evaluate(i / animationClip.frameRate);
                            if (curveBind.propertyName == "material._MainTex_ST.w")
                            {
                                if (i < MainTex_STy.Count)
                                {
                                    _bufferWriter.Write(1.0f - value - MainTex_STy[i]);
                                }
                                else
                                {
                                    _bufferWriter.Write(value);
                                }
                            }
                            else
                            {
                                _bufferWriter.Write(value);
                                if (curveBind.propertyName == "material._MainTex_ST.y")
                                {
                                    MainTex_STy.Add(value);
                                }
                            }
                        }
                    }
                }
            }

            foreach (var evt in animationClip.events)
            {
                var glTFFrameEvent = new AnimationFrameEvent();
                glTFFrameEvent.name           = evt.functionName;
                glTFFrameEvent.position       = evt.time;
                glTFFrameEvent.intVariable    = evt.intParameter;
                glTFFrameEvent.floatVariable  = evt.floatParameter;
                glTFFrameEvent.stringVariable = evt.stringParameter;
                ext.events.Add(glTFFrameEvent);
            }

            ext.events.Sort();
        }
Exemple #32
0
        public Buffer_Misc(List <string> imageList)
        {
            Model CreateModel(Action <List <Property>, AnimationChannel, Node> setProperties)
            {
                var properties = new List <Property>();

                // Apply the common properties to the glTF.
                var node = new Node
                {
                    Mesh = new Runtime.Mesh
                    {
                        MeshPrimitives = new[]
                        {
                            MeshPrimitive.CreateSinglePlane(includeTextureCoords: false)
                        }
                    },
                    Scale = new Vector3(0.8f)
                };

                var channel = new AnimationChannel();

                // Apply the proerties that are specific to this glTF
                setProperties(properties, channel, node);

                // Create the glTF object
                GLTF gltf = CreateGLTF(() => new Scene
                {
                    Nodes = new[]
                    {
                        node
                    },
                });

                gltf.Animations = new[]
                {
                    new Animation
                    {
                        Channels = new List <AnimationChannel>
                        {
                            channel
                        }
                    }
                };

                return(new Model
                {
                    Properties = properties,
                    GLTF = gltf,
                    Animated = true,
                    SeparateBuffers = true,
                });
            }

            void setTranslationChanneltarget(AnimationChannel channel, Node node)
            {
                channel.Target = new AnimationChannelTarget
                {
                    Node = node,
                    Path = AnimationChannelTargetPath.Translation,
                };
            }

            void SetLinearSamplerForTranslation(AnimationChannel channel)
            {
                channel.Sampler = new AnimationSampler
                {
                    Interpolation = AnimationSamplerInterpolation.Linear,
                    Input         = Data.Create(new[]
                    {
                        0.0f,
                        2.0f,
                        4.0f,
                    }),
                    Output = Data.Create(new[]
                    {
                        new Vector3(-0.1f, 0.0f, 0.0f),
                        new Vector3(0.1f, 0.0f, 0.0f),
                        new Vector3(-0.1f, 0.0f, 0.0f),
                    }),
                };
            }

            Models = new List <Model>
            {
                CreateModel((properties, channel, node) =>
                {
                    setTranslationChanneltarget(channel, node);
                    SetLinearSamplerForTranslation(channel);
                    properties.Add(new Property(PropertyName.Description, "The mesh primitive and animation data are stored in separate buffers."));
                })
            };

            GenerateUsedPropertiesList();
        }
Exemple #33
0
 public static bool Cross(AnimationChannel a, AnimationChannel b)
 {
     return((a & b) != 0);
 }
        /// <summary>
        /// Converts the specified animation to XNA.
        /// </summary>
        /// <param name="aiAnimation">The animation.</param>
        /// <returns>The animation converted to XNA.</returns>
        private AnimationContent ImportAnimation(Animation aiAnimation)
        {
            var animation = new AnimationContent
            {
                Name     = GetAnimationName(aiAnimation.Name),
                Identity = _identity,
                Duration = TimeSpan.FromSeconds(aiAnimation.DurationInTicks / aiAnimation.TicksPerSecond)
            };

            // In Assimp animation channels may be split into separate channels.
            //   "nodeXyz" --> "nodeXyz_$AssimpFbx$_Translation",
            //                 "nodeXyz_$AssimpFbx$_Rotation",
            //                 "nodeXyz_$AssimpFbx$_Scaling"
            // Group animation channels by name (strip the "_$AssimpFbx$" part).
            var channelGroups = aiAnimation.NodeAnimationChannels
                                .GroupBy(channel => GetNodeName(channel.NodeName));

            foreach (var channelGroup in channelGroups)
            {
                var boneName = channelGroup.Key;
                var channel  = new AnimationChannel();

                // Get transformation pivot for current bone.
                FbxPivot pivot;
                if (!_pivots.TryGetValue(boneName, out pivot))
                {
                    pivot = FbxPivot.Default;
                }

                var scaleKeys       = EmptyVectorKeys;
                var rotationKeys    = EmptyQuaternionKeys;
                var translationKeys = EmptyVectorKeys;

                foreach (var aiChannel in channelGroup)
                {
                    if (aiChannel.NodeName.EndsWith("_$AssimpFbx$_Scaling"))
                    {
                        scaleKeys = aiChannel.ScalingKeys;

                        Debug.Assert(pivot.Scaling.HasValue);
                        Debug.Assert(!aiChannel.HasRotationKeys || (aiChannel.RotationKeyCount == 1 && (aiChannel.RotationKeys[0].Value == new Assimp.Quaternion(1, 0, 0, 0) || aiChannel.RotationKeys[0].Value == new Assimp.Quaternion(0, 0, 0, 0))));
                        Debug.Assert(!aiChannel.HasPositionKeys || (aiChannel.PositionKeyCount == 1 && aiChannel.PositionKeys[0].Value == new Vector3D(0, 0, 0)));
                    }
                    else if (aiChannel.NodeName.EndsWith("_$AssimpFbx$_Rotation"))
                    {
                        rotationKeys = aiChannel.RotationKeys;

                        Debug.Assert(pivot.Rotation.HasValue);
                        Debug.Assert(!aiChannel.HasScalingKeys || (aiChannel.ScalingKeyCount == 1 && aiChannel.ScalingKeys[0].Value == new Vector3D(1, 1, 1)));
                        Debug.Assert(!aiChannel.HasPositionKeys || (aiChannel.PositionKeyCount == 1 && aiChannel.PositionKeys[0].Value == new Vector3D(0, 0, 0)));
                    }
                    else if (aiChannel.NodeName.EndsWith("_$AssimpFbx$_Translation"))
                    {
                        translationKeys = aiChannel.PositionKeys;

                        Debug.Assert(pivot.Translation.HasValue);
                        Debug.Assert(!aiChannel.HasScalingKeys || (aiChannel.ScalingKeyCount == 1 && aiChannel.ScalingKeys[0].Value == new Vector3D(1, 1, 1)));
                        Debug.Assert(!aiChannel.HasRotationKeys || (aiChannel.RotationKeyCount == 1 && (aiChannel.RotationKeys[0].Value == new Assimp.Quaternion(1, 0, 0, 0) || aiChannel.RotationKeys[0].Value == new Assimp.Quaternion(0, 0, 0, 0))));
                    }
                    else
                    {
                        scaleKeys       = aiChannel.ScalingKeys;
                        rotationKeys    = aiChannel.RotationKeys;
                        translationKeys = aiChannel.PositionKeys;
                    }
                }

                // Get all unique keyframe times. (Assuming that no two key frames
                // have the same time, which is usually a safe assumption.)
                var times = scaleKeys.Select(k => k.Time)
                            .Union(rotationKeys.Select(k => k.Time))
                            .Union(translationKeys.Select(k => k.Time))
                            .OrderBy(t => t)
                            .ToList();

                Debug.Assert(times.Count == times.Distinct().Count(), "Sequences combined with Union() should not have duplicates.");

                int        prevScaleIndex       = -1;
                int        prevRotationIndex    = -1;
                int        prevTranslationIndex = -1;
                double     prevScaleTime        = 0.0;
                double     prevRotationTime     = 0.0;
                double     prevTranslationTime  = 0.0;
                Vector3?   prevScale            = null;
                Quaternion?prevRotation         = null;
                Vector3?   prevTranslation      = null;

                foreach (var time in times)
                {
                    // Get scaling.
                    Vector3?scale;
                    int     scaleIndex = scaleKeys.FindIndex(k => k.Time == time);
                    if (scaleIndex != -1)
                    {
                        // Scaling key found.
                        scale          = ToXna(scaleKeys[scaleIndex].Value);
                        prevScaleIndex = scaleIndex;
                        prevScaleTime  = time;
                        prevScale      = scale;
                    }
                    else
                    {
                        // No scaling key found.
                        if (prevScaleIndex != -1 && prevScaleIndex + 1 < scaleKeys.Count)
                        {
                            // Lerp between previous and next scaling key.
                            var nextScaleKey  = scaleKeys[prevScaleIndex + 1];
                            var nextScaleTime = nextScaleKey.Time;
                            var nextScale     = ToXna(nextScaleKey.Value);
                            var amount        = (float)((time - prevScaleTime) / (nextScaleTime - prevScaleTime));
                            scale = Vector3.Lerp(prevScale.Value, nextScale, amount);
                        }
                        else
                        {
                            // Hold previous scaling value.
                            scale = prevScale;
                        }
                    }

                    // Get rotation.
                    Quaternion?rotation;
                    int        rotationIndex = rotationKeys.FindIndex(k => k.Time == time);
                    if (rotationIndex != -1)
                    {
                        // Rotation key found.
                        rotation          = ToXna(rotationKeys[rotationIndex].Value);
                        prevRotationIndex = rotationIndex;
                        prevRotationTime  = time;
                        prevRotation      = rotation;
                    }
                    else
                    {
                        // No rotation key found.
                        if (prevRotationIndex != -1 && prevRotationIndex + 1 < rotationKeys.Count)
                        {
                            // Lerp between previous and next rotation key.
                            var nextRotationKey  = rotationKeys[prevRotationIndex + 1];
                            var nextRotationTime = nextRotationKey.Time;
                            var nextRotation     = ToXna(nextRotationKey.Value);
                            var amount           = (float)((time - prevRotationTime) / (nextRotationTime - prevRotationTime));
                            rotation = Quaternion.Slerp(prevRotation.Value, nextRotation, amount);
                        }
                        else
                        {
                            // Hold previous rotation value.
                            rotation = prevRotation;
                        }
                    }

                    // Get translation.
                    Vector3?translation;
                    int     translationIndex = translationKeys.FindIndex(k => k.Time == time);
                    if (translationIndex != -1)
                    {
                        // Translation key found.
                        translation          = ToXna(translationKeys[translationIndex].Value);
                        prevTranslationIndex = translationIndex;
                        prevTranslationTime  = time;
                        prevTranslation      = translation;
                    }
                    else
                    {
                        // No translation key found.
                        if (prevTranslationIndex != -1 && prevTranslationIndex + 1 < translationKeys.Count)
                        {
                            // Lerp between previous and next translation key.
                            var nextTranslationKey  = translationKeys[prevTranslationIndex + 1];
                            var nextTranslationTime = nextTranslationKey.Time;
                            var nextTranslation     = ToXna(nextTranslationKey.Value);
                            var amount = (float)((time - prevTranslationTime) / (nextTranslationTime - prevTranslationTime));
                            translation = Vector3.Lerp(prevTranslation.Value, nextTranslation, amount);
                        }
                        else
                        {
                            // Hold previous translation value.
                            translation = prevTranslation;
                        }
                    }

                    // Apply transformation pivot.
                    var transform = Matrix.Identity;

                    if (pivot.GeometricScaling.HasValue)
                    {
                        transform *= pivot.GeometricScaling.Value;
                    }
                    if (pivot.GeometricRotation.HasValue)
                    {
                        transform *= pivot.GeometricRotation.Value;
                    }
                    if (pivot.GeometricTranslation.HasValue)
                    {
                        transform *= pivot.GeometricTranslation.Value;
                    }
                    if (pivot.ScalingPivotInverse.HasValue)
                    {
                        transform *= pivot.ScalingPivotInverse.Value;
                    }
                    if (scale.HasValue)
                    {
                        transform *= Matrix.CreateScale(scale.Value);
                    }
                    else if (pivot.Scaling.HasValue)
                    {
                        transform *= pivot.Scaling.Value;
                    }
                    if (pivot.ScalingPivot.HasValue)
                    {
                        transform *= pivot.ScalingPivot.Value;
                    }
                    if (pivot.ScalingOffset.HasValue)
                    {
                        transform *= pivot.ScalingOffset.Value;
                    }
                    if (pivot.RotationPivotInverse.HasValue)
                    {
                        transform *= pivot.RotationPivotInverse.Value;
                    }
                    if (pivot.PostRotation.HasValue)
                    {
                        transform *= pivot.PostRotation.Value;
                    }
                    if (rotation.HasValue)
                    {
                        transform *= Matrix.CreateFromQuaternion(rotation.Value);
                    }
                    else if (pivot.Rotation.HasValue)
                    {
                        transform *= pivot.Rotation.Value;
                    }
                    if (pivot.PreRotation.HasValue)
                    {
                        transform *= pivot.PreRotation.Value;
                    }
                    if (pivot.RotationPivot.HasValue)
                    {
                        transform *= pivot.RotationPivot.Value;
                    }
                    if (pivot.RotationOffset.HasValue)
                    {
                        transform *= pivot.RotationOffset.Value;
                    }
                    if (translation.HasValue)
                    {
                        transform *= Matrix.CreateTranslation(translation.Value);
                    }
                    else if (pivot.Translation.HasValue)
                    {
                        transform *= pivot.Translation.Value;
                    }

                    channel.Add(new AnimationKeyframe(TimeSpan.FromSeconds(time / aiAnimation.TicksPerSecond), transform));
                }

                animation.Channels[channelGroup.Key] = channel;
            }

            return(animation);
        }
        /// <summary>
        /// Called when an XML document is read that specifies how animations
        /// should be split.
        /// </summary>
        /// <param name="animDict">The dictionary of animation name/AnimationContent
        /// pairs. </param>
        /// <param name="doc">The Xml document that contains info on how to split
        /// the animations.</param>
        protected virtual void SubdivideAnimations(
            AnimationContentDictionary animDict, XmlDocument doc)
        {
            string[] animNames = new string[animDict.Keys.Count];
            animDict.Keys.CopyTo(animNames, 0);
            if (animNames.Length == 0)
                return;

            // Traverse each xml node that represents an animation to be subdivided
            foreach (XmlNode node in doc)
            {
                XmlElement child = node as XmlElement;
                if (child == null || child.Name != "animation")
                    continue;

                string animName = null;
                if (child["name"] != null)
                {
                    // The name of the animation to be split
                    animName = child["name"].InnerText;
                }
                else if (child["index"] != null)
                {
                    animName = animNames[int.Parse(child["index"].InnerText)];
                }
                else
                {
                    animName = animNames[0];
                }

                // If the tickspersecond node is filled, use that to calculate seconds per tick
                double animTicksPerSecond = 1.0, secondsPerTick = 0;
                try
                {
                    if (child["tickspersecond"] != null)
                    {
                        animTicksPerSecond = double.Parse(child["tickspersecond"].InnerText);
                    }
                }
                catch
                {
                    throw new Exception("Error parsing tickspersecond in xml file.");
                }
                if (animTicksPerSecond <= 0)
                    throw new InvalidDataException("AnimTicksPerSecond in XML file must be " +
                        "a positive number.");
                secondsPerTick = 1.0 / animTicksPerSecond;

                AnimationContent anim = null;
                // Get the animation and remove it from the dict
                // Check to see if the animation specified in the xml file exists
                try
                {
                    anim = animDict[animName];
                }
                catch
                {
                    throw new Exception("Animation named " + animName + " specified in XML file does not exist in model.");
                }
                animDict.Remove(anim.Name);
                // Get the list of new animations
                XmlNodeList subAnimations = child.GetElementsByTagName("animationsubset");

                foreach (XmlElement subAnim in subAnimations)
                {
                    // Create the new sub animation
                    AnimationContent newAnim = new AnimationContent();
                    XmlElement subAnimNameElement = subAnim["name"];

                    if (subAnimNameElement != null)
                        newAnim.Name = subAnimNameElement.InnerText;

                    // If a starttime node exists, use that to get the start time
                    long startTime, endTime;
                    if (subAnim["starttime"] != null)
                    {
                        try
                        {
                            startTime = TimeSpan.FromSeconds(double.Parse(subAnim["starttime"].InnerText)).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing starttime node in XML file.  Node inner text "
                                + "must be a non negative number.");
                        }
                    }
                    else if (subAnim["startframe"] != null)// else use the secondspertick combined with the startframe node value
                    {
                        try
                        {
                            double seconds =
                                double.Parse(subAnim["startframe"].InnerText) * secondsPerTick;

                            startTime = TimeSpan.FromSeconds(
                                seconds).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing startframe node in XML file.  Node inner text "
                              + "must be a non negative number.");
                        }
                    }
                    else
                        throw new Exception("Sub animation in XML file must have either a starttime or startframe node.");

                    // Same with endtime/endframe
                    if (subAnim["endtime"] != null)
                    {
                        try
                        {
                            endTime = TimeSpan.FromSeconds(double.Parse(subAnim["endtime"].InnerText)).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing endtime node in XML file.  Node inner text "
                                + "must be a non negative number.");
                        }
                    }
                    else if (subAnim["endframe"] != null)
                    {
                        try
                        {
                            double seconds = double.Parse(subAnim["endframe"].InnerText)
                                * secondsPerTick;
                            endTime = TimeSpan.FromSeconds(
                                seconds).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing endframe node in XML file.  Node inner text "
                                + "must be a non negative number.");
                        }
                    }
                    else
                        throw new Exception("Sub animation in XML file must have either an endtime or endframe node.");

                    if (endTime < startTime)
                        throw new Exception("Start time must be <= end time in XML file.");

                    // Now that we have the start and end times, we associate them with
                    // start and end indices for each animation track/channel
                    foreach (KeyValuePair<string, AnimationChannel> k in anim.Channels)
                    {
                        // The current difference between the start time and the
                        // time at the current index
                        long currentStartDiff;
                        // The current difference between the end time and the
                        // time at the current index
                        long currentEndDiff;
                        // The difference between the start time and the time
                        // at the start index
                        long bestStartDiff=long.MaxValue;
                        // The difference between the end time and the time at
                        // the end index
                        long bestEndDiff=long.MaxValue;

                        // The start and end indices
                        int startIndex = -1;
                        int endIndex = -1;

                        // Create a new channel and reference the old channel
                        AnimationChannel newChan = new AnimationChannel();
                        AnimationChannel oldChan = k.Value;

                        // Iterate through the keyframes in the channel
                        for (int i = 0; i < oldChan.Count; i++)
                        {
                            // Update the startIndex, endIndex, bestStartDiff,
                            // and bestEndDiff
                            long ticks = oldChan[i].Time.Ticks;
                            currentStartDiff = Math.Abs(startTime - ticks);
                            currentEndDiff = Math.Abs(endTime - ticks);
                            if (startIndex == -1 || currentStartDiff<bestStartDiff)
                            {
                                startIndex = i;
                                bestStartDiff = currentStartDiff;
                            }
                            if (endIndex == -1 || currentEndDiff<bestEndDiff)
                            {
                                endIndex = i;
                                bestEndDiff = currentEndDiff;
                            }
                        }

                        // Now we have our start and end index for the channel
                        for (int i = startIndex; i <= endIndex; i++)
                        {
                            AnimationKeyframe frame = oldChan[i];
                            long time;
                            // Clamp the time so that it can't be less than the
                            // start time
                            if (frame.Time.Ticks < startTime)
                                time = 0;
                            // Clamp the time so that it can't be greater than the
                            // end time
                            else if (frame.Time.Ticks > endTime)
                                time = endTime - startTime;
                            else // Else get the time
                                time = frame.Time.Ticks - startTime;

                            // Finally... create the new keyframe and add it to the new channel
                            AnimationKeyframe keyframe = new AnimationKeyframe(
                                TimeSpan.FromTicks(time),
                                frame.Transform);

                            newChan.Add(keyframe);
                        }

                        // Add the channel and update the animation duration based on the
                        // length of the animation track.
                        newAnim.Channels.Add(k.Key, newChan);
                        if (newChan[newChan.Count - 1].Time > newAnim.Duration)
                            newAnim.Duration = newChan[newChan.Count - 1].Time;

                    }
                    try
                    {
                        // Add the subdived animation to the dictionary.
                        animDict.Add(newAnim.Name, newAnim);
                    }
                    catch
                    {
                        throw new Exception("Attempt to add an animation when one by the same name already exists. " +
                            "Name: " + newAnim.Name);
                    }
                }

            }
        }
        /// <summary>
        /// Retrieves and interpolates the pose of an animation channel.
        /// </summary>
        /// <param name="animationChannel">Name of the animation channel.</param>
        /// <param name="animationTime">Current animation clip time.</param>
        /// <param name="outPose">The output interpolated pose.</param>
        private void InterpolateChannelPose(AnimationChannel animationChannel, TimeSpan animationTime,
            out Pose outPose)
        {
            if (translationInterpolation == InterpolationMode.None &&
                orientationInterpolation == InterpolationMode.None &&
                    scaleInterpolation == InterpolationMode.None)
            {
                int keyframeIndex = animationChannel.GetKeyframeIndexByTime(animationTime);
                outPose = animationChannel[keyframeIndex].Pose;
            }
            else
            {
                int keyframeIndex = animationChannel.GetKeyframeIndexByTime(animationTime);
                int nextKeyframeIndex = (keyframeIndex + 1) % animationChannel.Count;

                AnimationChannelKeyframe keyframe1 = animationChannel[keyframeIndex];
                AnimationChannelKeyframe keyframe2 = animationChannel[nextKeyframeIndex];

                // Calculate the time between the keyframes considering loop
                long keyframeDuration;
                if (keyframeIndex == (animationChannel.Count - 1))
                    keyframeDuration = animationClip.Duration.Ticks - keyframe1.Time.Ticks;
                else
                    keyframeDuration = keyframe2.Time.Ticks - keyframe1.Time.Ticks;

                // Interpolate when duration higher than zero
                if (keyframeDuration > 0)
                {
                    long elapsedKeyframeTime = animationTime.Ticks - keyframe1.Time.Ticks;
                    float lerpFactor = elapsedKeyframeTime / (float)keyframeDuration;

                    outPose =
                        Pose.Interpolate(keyframe1.Pose, keyframe2.Pose, lerpFactor,
                            translationInterpolation, orientationInterpolation, scaleInterpolation);
                }
                // Otherwise don't interpolate
                else
                    outPose = keyframe1.Pose;
            }
        }
Exemple #37
0
        void ProcessAnimation(AnimationContent anim, ContentProcessorContext context, List <AnimationData> animations, Dictionary <string, int> indices, SkeletonData skeleton)
        {
            SortedDictionary <TimeSpan, bool>     allFrameTimes = new SortedDictionary <TimeSpan, bool>();
            SortedDictionary <TimeSpan, Matrix[]> transforms    = new SortedDictionary <TimeSpan, Matrix[]>();
            int totalChannels = 0;

            foreach (KeyValuePair <string, AnimationChannel> channelKVP in anim.Channels)
            {
                if (indices.ContainsKey(CleanBoneName(channelKVP.Key)) == false)
                {
                    continue;
                }

                AnimationChannel channel = channelKVP.Value;

                foreach (AnimationKeyframe frame in channel)
                {
                    if (allFrameTimes.ContainsKey(frame.Time) == false)
                    {
                        allFrameTimes.Add(frame.Time, true);
                    }
                }
                totalChannels++;
            }

            foreach (TimeSpan time in allFrameTimes.Keys)
            {
                transforms.Add(time, new Matrix[totalChannels]);
            }

            SortedDictionary <TimeSpan, Matrix>     keyFrames = new SortedDictionary <TimeSpan, Matrix>();
            List <KeyValuePair <TimeSpan, Matrix> > newFrames = new List <KeyValuePair <TimeSpan, Matrix> >();

            int index = 0;

            foreach (KeyValuePair <string, AnimationChannel> channelKVP in anim.Channels)
            {
                int boneIndex = 0;
                if (indices.TryGetValue(CleanBoneName(channelKVP.Key), out boneIndex) == false)
                {
                    continue;
                }

                AnimationChannel channel = channelKVP.Value;
                Matrix           transform;

                foreach (AnimationKeyframe frame in channel)
                {
                    transform = frame.Transform;

                    if (boneIndex == 0)
                    {
                        CorrectRootBoneTransform(ref transform, skeleton);
                    }
                    else
                    {
                        //remove translation
                        transform.Translation = new Vector3();
                    }

                    keyFrames.Add(frame.Time, transform);
                }

                SortedDictionary <TimeSpan, Matrix> .Enumerator frames = keyFrames.GetEnumerator();
                SortedDictionary <TimeSpan, bool> .Enumerator   times  = allFrameTimes.GetEnumerator();

                if (!times.MoveNext())
                {
                    continue;
                }
                if (!frames.MoveNext())
                {
                    continue;
                }
                TimeSpan time = frames.Current.Key;
                transform = frames.Current.Value;

                while (true)
                {
                    Matrix   previousTransform = transform;
                    TimeSpan previousTime      = time;

                    time      = frames.Current.Key;
                    transform = frames.Current.Value;

                    if (times.Current.Key.Ticks == frames.Current.Key.Ticks)
                    {
                        if (!times.MoveNext())
                        {
                            break;
                        }

                        if (!frames.MoveNext())
                        {
                            //frames ends early...
                            while (true)
                            {
                                newFrames.Add(new KeyValuePair <TimeSpan, Matrix>(times.Current.Key, transform));

                                if (!times.MoveNext())
                                {
                                    break;
                                }
                            }
                            break;
                        }
                        continue;
                    }

                    if (times.Current.Key.Ticks > frames.Current.Key.Ticks)
                    {
                        //frame is behind
                        if (!frames.MoveNext())
                        {
                            //frames ends early...
                            while (true)
                            {
                                newFrames.Add(new KeyValuePair <TimeSpan, Matrix>(times.Current.Key, transform));

                                if (!times.MoveNext())
                                {
                                    break;
                                }
                            }
                            break;
                        }
                        continue;
                    }
                    else
                    {
                        //frame is ahead.. create an inbetween

                        double amount       = (times.Current.Key - previousTime).TotalSeconds / (frames.Current.Key - previousTime).TotalSeconds;
                        Matrix newTransform = Matrix.Lerp(previousTransform, frames.Current.Value, (float)amount);
                        //Matrix newTransform = frames.Current.Value.Interpolate(ref previousTransform, (float)(1 - amount));
                        newFrames.Add(new KeyValuePair <TimeSpan, Matrix>(times.Current.Key, newTransform));

                        transform = newTransform;
                        time      = times.Current.Key;

                        if (!times.MoveNext())
                        {
                            break;
                        }
                    }
                }


                foreach (KeyValuePair <TimeSpan, Matrix> newFrame in newFrames)
                {
                    keyFrames.Add(newFrame.Key, newFrame.Value);
                }

                foreach (KeyValuePair <TimeSpan, Matrix> kvp in keyFrames)
                {
                    Matrix[] array;
                    if (transforms.TryGetValue(kvp.Key, out array))
                    {
                        array[index] = kvp.Value;
                    }
                    else
                    {
                        continue;
                    }
                }

                newFrames.Clear();
                keyFrames.Clear();

                index++;
            }

            KeyFrameData[] boneKeyFrames = new KeyFrameData[transforms.Count];
            int[]          boneIndices   = new int[totalChannels];
            index = 0;


            foreach (KeyValuePair <string, AnimationChannel> channelKVP in anim.Channels)
            {
                if (indices.ContainsKey(CleanBoneName(channelKVP.Key)) == false)
                {
                    continue;
                }
                boneIndices[index++] = indices[CleanBoneName(channelKVP.Key)];
            }

            //compute world space bone default skeleton
            Matrix[] worldBoneTransforms = skeleton.BoneLocalMatrices.ToArray();
            skeleton.TransformHierarchy(worldBoneTransforms);

            index = 0;
            float duration = 0;

            foreach (KeyValuePair <TimeSpan, Matrix[]> kvp in transforms)
            {
                float seconds = (float)kvp.Key.TotalSeconds;

                /*
                 * //make transforms realtive to skeleton
                 * //first get static bone transforms
                 * Matrix[] worldTransform = new Matrix[skeleton.BoneCount];
                 * for (int i = 0; i < worldTransform.Length; i++)
                 *      worldTransform[i] = skeleton.BoneLocalMatrices[i];
                 *
                 * //replace the animated bones..
                 * for (int i = 0; i < kvp.Value.Length; i++)
                 *      worldTransform[boneIndices[i]] = kvp.Value[i];
                 *
                 * //transform into a world space skeleton
                 * skeleton.TransformHierarchy(worldTransform);
                 *
                 * //multiply the world space transforms with the inverse of the world space static skeleton
                 * for (int i = 0; i < worldTransform.Length; i++)
                 * {
                 *      Matrix m = worldBoneTransforms[i];
                 *      Matrix.Invert(ref m, out m);
                 *
                 *      worldTransform[i] = m * worldTransform[i];
                 * }
                 *
                 * //transform back out of world space into joint space
                 * skeleton.TransformHierarchyInverse(worldTransform);
                 *
                 *
                 * for (int i = 0; i < kvp.Value.Length; i++)
                 *      kvp.Value[i] = worldTransform[boneIndices[i]];
                 */
                boneKeyFrames[index++] = new KeyFrameData(seconds, kvp.Value);
                duration = Math.Max(seconds, duration);
            }

            if (boneIndices.Length > 0 && boneKeyFrames.Length > 0)
            {
                AnimationData animation = new AnimationData(anim.Name, boneIndices, boneKeyFrames, duration, animationCompressionTolerancePercent);

                animations.Add(animation);
            }
        }
        /// <summary>
        /// Interpolates an AnimationContent object to 60 fps.
        /// </summary>
        /// <param name="input">The AnimationContent to interpolate.</param>
        /// <returns>The interpolated AnimationContent.</returns>
        public virtual AnimationContent Interpolate(AnimationContent input)
        {
            AnimationContent output            = new AnimationContent();
            long             time              = 0;
            long             animationDuration = input.Duration.Ticks;

            // default XNA importers, due to floating point errors or TimeSpan
            // estimation, sometimes  have channels with a duration slightly longer than
            // the animation duration.  So, set the animation duration to its true
            // value
            foreach (KeyValuePair <string, AnimationChannel> c in input.Channels)
            {
                if (c.Value[c.Value.Count - 1].Time.Ticks > animationDuration)
                {
                    animationDuration = c.Value[c.Value.Count - 1].Time.Ticks;
                }
            }

            foreach (KeyValuePair <string, AnimationChannel> c in input.Channels)
            {
                time = 0;
                string           channelName = c.Key;
                AnimationChannel channel     = c.Value;
                AnimationChannel outChannel  = new AnimationChannel();
                int currentFrame             = 0;

                // Step through time until the time passes the animation duration
                while (time <= animationDuration)
                {
                    AnimationKeyframe keyframe;
                    // Clamp the time to the duration of the animation and make this
                    // keyframe equal to the last animation frame.
                    if (time >= animationDuration)
                    {
                        time     = animationDuration;
                        keyframe = new AnimationKeyframe(new TimeSpan(time),
                                                         channel[channel.Count - 1].Transform);
                    }
                    else
                    {
                        // If the channel only has one keyframe, set the transform for the current time
                        // to that keyframes transform
                        if (channel.Count == 1 || time < channel[0].Time.Ticks)
                        {
                            keyframe = new AnimationKeyframe(new TimeSpan(time), channel[0].Transform);
                        }
                        // If the current track duration is less than the animation duration,
                        // use the last transform in the track once the time surpasses the duration
                        else if (channel[channel.Count - 1].Time.Ticks <= time)
                        {
                            keyframe = new AnimationKeyframe(new TimeSpan(time), channel[channel.Count - 1].Transform);
                        }
                        else // proceed as normal
                        {
                            // Go to the next frame that is less than the current time
                            while (channel[currentFrame + 1].Time.Ticks < time)
                            {
                                currentFrame++;
                            }
                            // Numerator of the interpolation factor
                            double interpNumerator = (double)(time - channel[currentFrame].Time.Ticks);
                            // Denominator of the interpolation factor
                            double interpDenom = (double)(channel[currentFrame + 1].Time.Ticks - channel[currentFrame].Time.Ticks);
                            // The interpolation factor, or amount to interpolate between the current
                            // and next frame
                            double interpAmount = interpNumerator / interpDenom;

                            // If the frames are roughly 60 frames per second apart, use linear interpolation
                            if (channel[currentFrame + 1].Time.Ticks - channel[currentFrame].Time.Ticks
                                <= ContentUtil.TICKS_PER_60FPS * 1.05)
                            {
                                keyframe = new AnimationKeyframe(new TimeSpan(time),
                                                                 Matrix.Lerp(
                                                                     channel[currentFrame].Transform,
                                                                     channel[currentFrame + 1].Transform,
                                                                     (float)interpAmount));
                            }
                            else // else if the transforms between the current frame and the next aren't identical
                                 // decompose the matrix and interpolate the rotation separately
                            if (channel[currentFrame].Transform != channel[currentFrame + 1].Transform)
                            {
                                keyframe = new AnimationKeyframe(new TimeSpan(time),
                                                                 ContentUtil.SlerpMatrix(
                                                                     channel[currentFrame].Transform,
                                                                     channel[currentFrame + 1].Transform,
                                                                     (float)interpAmount));
                            }
                            else // Else the adjacent frames have identical transforms and we can use
                                 // the current frames transform for the current keyframe.
                            {
                                keyframe = new AnimationKeyframe(new TimeSpan(time),
                                                                 channel[currentFrame].Transform);
                            }
                        }
                    }
                    // Add the interpolated keyframe to the new channel.
                    outChannel.Add(keyframe);
                    // Step the time forward by 1/60th of a second
                    time += ContentUtil.TICKS_PER_60FPS;
                }

                // Compensate for the time error,(animation duration % TICKS_PER_60FPS),
                // caused by the interpolation by setting the last keyframe in the
                // channel to the animation duration.
                if (outChannel[outChannel.Count - 1].Time.Ticks < animationDuration)
                {
                    outChannel.Add(new AnimationKeyframe(
                                       TimeSpan.FromTicks(animationDuration),
                                       channel[channel.Count - 1].Transform));
                }

                outChannel.Add(new AnimationKeyframe(input.Duration,
                                                     channel[channel.Count - 1].Transform));
                // Add the interpolated channel to the animation
                output.Channels.Add(channelName, outChannel);
            }
            // Set the interpolated duration to equal the inputs duration for consistency
            output.Duration = TimeSpan.FromTicks(animationDuration);
            return(output);
        }
Exemple #39
0
        internal static Mesh3D LoadFromFileSUB(Device device, Assimp.Scene pScene, InputElement[] ieLayout, List <SceneObject> meshArr)
        {
            string sourceTextures = ConfigurationSettings.AppSettings["SourceTextures"];

            int iVBufferSize = 0;
            int iIBufferSize = 0;

            foreach (SceneObject so in meshArr)
            {
                iVBufferSize += (int)so.pMesh.VertexCount;
                iIBufferSize += (int)so.pMesh.FaceCount * 3;
            }


            MeshInputElements10.sNormalMesh[] tVertex = new MeshInputElements10.sNormalMesh[iVBufferSize];
            //short[] tIndex = new short[iIBufferSize];
            uint[] tIndex = new uint[iIBufferSize];
            MeshAttributeRange[] tAttibutes = new MeshAttributeRange[meshArr.Count];
            MeshNode[]           nodes      = new MeshNode[meshArr.Count];
            string[]             tTextures  = new string[meshArr.Count];

            // Monitor global poisition in the vertex, index and attribute buffers.
            int iAttribute    = 0;
            int iBVertex      = 0;
            int iBIndex       = 0;
            int prevVertCount = 0;

            foreach (SceneObject so in meshArr)
            {
                MeshNode n = new MeshNode();
                n.attributeId     = iAttribute;
                n.nodeName        = so.nodeName;
                n.parentNodeName  = so.parentNodeName;
                n.transform       = toDXMat(so.transform);
                nodes[iAttribute] = n;


                MeshAttributeRange pAttrib = new MeshAttributeRange();
                pAttrib.Id          = iAttribute;
                pAttrib.VertexStart = iBVertex;
                pAttrib.FaceStart   = iBIndex / 3;

                Assimp.Material mat = pScene.Materials[so.pMesh.MaterialIndex];
                if (mat.GetTextureCount(Assimp.TextureType.Diffuse) > 0)
                {
                    string inputFilePath = mat.GetTexture(Assimp.TextureType.Diffuse, 0).FilePath.Replace("\\\\", "\\");
                    string inputFileName = Path.GetFileName(inputFilePath);
                    string ext           = Path.GetExtension(inputFileName);
                    string subFolder     = inputFilePath.Replace(sourceTextures, "").Split('\\')[1];
                    string diffOutFile   = subFolder + "\\" + inputFileName.Replace(ext, ".dds");
                    tTextures[iAttribute] = diffOutFile;
                }

                // Copy verticies.
                int iMeshVerts = (int)so.pMesh.VertexCount;
                for (int iVertex = 0; iVertex < iMeshVerts; ++iVertex)
                {
                    tVertex[iVertex + iBVertex] = getVert(so, iVertex);
                }

                // Increment the vertex count by the number of verticies we just looped over.
                iBVertex += iMeshVerts;

                // Copy indicies.
                int iMeshFaces = (int)so.pMesh.FaceCount;
                for (int iFace = 0; iFace < iMeshFaces; ++iFace)
                {
                    uint[] tIndices = so.pMesh.Faces[iFace].Indices;
                    //tIndex[iBIndex++] = Convert.ToInt16(tIndices[0] + (iAttribute == 0 ? 0 : prevVertCount));
                    //tIndex[iBIndex++] = Convert.ToInt16(tIndices[1] + (iAttribute == 0 ? 0 : prevVertCount));
                    //tIndex[iBIndex++] = Convert.ToInt16(tIndices[2] + (iAttribute == 0 ? 0 : prevVertCount));
                    tIndex[iBIndex++] = Convert.ToUInt32(tIndices[0] + (iAttribute == 0 ? 0 : prevVertCount));
                    tIndex[iBIndex++] = Convert.ToUInt32(tIndices[1] + (iAttribute == 0 ? 0 : prevVertCount));
                    tIndex[iBIndex++] = Convert.ToUInt32(tIndices[2] + (iAttribute == 0 ? 0 : prevVertCount));
                }

                // Increment the face count by the number of faces we just looped over.
                prevVertCount += iMeshVerts;


                pAttrib.FaceCount      = iMeshFaces;
                pAttrib.VertexCount    = iMeshVerts;
                tAttibutes[iAttribute] = pAttrib;
                iAttribute++;
            }

            Mesh3D gm     = new Mesh3D();
            Mesh   output = new Mesh(device, ieLayout, ieLayout[0].SemanticName, iVBufferSize, iIBufferSize / 3, MeshFlags.Has32BitIndices);

            output.SetAttributeTable(tAttibutes);

            DataStream verts = new DataStream(iVBufferSize * Marshal.SizeOf(typeof(MeshInputElements10.sNormalMesh)), true, true);
            //DataStream indicies = new DataStream(iIBufferSize * Marshal.SizeOf(typeof(short)), true, true);
            DataStream indicies = new DataStream(iIBufferSize * Marshal.SizeOf(typeof(uint)), true, true);

            verts.WriteRange(tVertex);
            indicies.WriteRange(tIndex);


            verts.Position    = 0;
            indicies.Position = 0;

            output.SetVertexData(0, verts);
            output.SetIndexData(indicies, iIBufferSize);
            output.Commit();
            verts.Dispose();
            indicies.Dispose();

            gm.attrTable     = tAttibutes;
            gm.meshObj       = output;
            gm.NumAttributes = meshArr.Count;
            gm.materials     = tTextures;
            gm.nodes         = nodes;

            if (pScene.HasAnimations)
            {
                gm.animations = new List <Mesh3DAnimation>();
                foreach (Assimp.Animation a in pScene.Animations)
                {
                    Mesh3DAnimation anim = new Mesh3DAnimation();
                    anim.animName = a.Name;
                    anim.duration = a.DurationInTicks;
                    anim.channels = new AnimationChannel[a.NodeAnimationChannelCount];

                    int x = 0;
                    foreach (Assimp.NodeAnimationChannel c in a.NodeAnimationChannels)
                    {
                        if (c == null)
                        {
                            continue;
                        }

                        AnimationChannel ac = new AnimationChannel();
                        ac.nodeName = c.NodeName;
                        ac.animKeys = new AnimationKey[c.RotationKeyCount];
                        for (int i = 0; i < c.PositionKeys.Length; i++)
                        {
                            double      time = 0;
                            SceneObject sObj = meshArr[0];
                            foreach (SceneObject so in meshArr)
                            {
                                if (so.nodeName == c.NodeName)
                                {
                                    sObj = so;
                                    break;
                                }
                            }

                            time = c.PositionKeys[i].Time;
                            Matrix  mat            = toDXMat(sObj.transform);
                            Vector3 rotationCenter = new Vector3(mat.M41, mat.M42, mat.M43);
                            mat.Invert();
                            Vector3 pos = Vector3.TransformCoordinate(toDX(c.PositionKeys[i].Value), mat);


                            Matrix matAnim = Matrix.Transformation(Vector3.Zero, Quaternion.Identity, new Vector3(1, 1, 1), rotationCenter, toDX(c.RotationKeys[i].Value), pos);


                            AnimationKey ak = new AnimationKey();
                            ak.animMat     = matAnim;
                            ak.time        = time;
                            ac.animKeys[i] = ak;
                        }

                        anim.channels[x++] = ac;
                    }

                    gm.animations.Add(anim);
                }
            }



            return(gm);
        }