示例#1
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);
        }
示例#2
0
        /// <summary>
        /// Writes a ModelInfo object into XNB data
        /// </summary>
        /// <param name="output">The stream that contains the written data</param>
        /// <param name="value">The instance to be serialized</param>
        protected override void Write(ContentWriter output, AnimationContentDictionary value)
        {
            AnimationContentDictionary animations = value;

            output.Write(animations.Count);

            foreach (KeyValuePair <string, AnimationContent> k in animations)
            {
                output.Write(k.Key);

                output.Write(k.Value.Channels.Count);
                foreach (KeyValuePair <string, AnimationChannel> chan in k.Value.Channels)
                {
                    output.Write(chan.Key);

                    int numKeys  = chan.Value.Count;
                    int halfKeys = numKeys > 0 ? numKeys / 2 + 1 : 0;

                    long duration = chan.Value.Count > 0
                        ? chan.Value[chan.Value.Count - 1].Time.Ticks
                        : 0;

                    output.Write(halfKeys);
                    output.Write(duration);

                    for (int i = 0; i < halfKeys; ++i)
                    {
                        int idx = i * 2 < numKeys ? i * 2 : numKeys - 1;
                        AnimationKeyframe keyframe = chan.Value[idx];
                        output.Write(keyframe.Transform);
                    }
                }
            }
        }
示例#3
0
        /*
         * template AnimationKey
         * {
         *     DWORD keyType;
         *     DWORD nKeys;
         *     array TimedFloatKeys keys[nKeys];
         * }
         *
         *
         * template TimedFloatKeys
         * {
         *     DWORD time;
         *     FloatKeys tfkeys;
         * }
         *
         * template FloatKeys
         * {
         *     DWORD nValues;
         *     array float values[nValues];
         * }
         */
        /// <summary>
        ///  Imports a key frame list associated with an animation channel
        /// </summary>
        /// <param name="keyType">The type of animation keys used by the current channel</param>
        /// <returns>The list of key frames for the given key type in the current channel</returns>
        private AnimationKeyframe[] ImportAnimationKey(out int keyType)
        {
            // These keys can be rotation (0),scale(1),translation(2), or matrix(3 or 4) keys.
            keyType = tokens.SkipName().NextInt();
            // Number of frames in channel
            int numFrames = tokens.NextInt();

            AnimationKeyframe[] frames = new AnimationKeyframe[numFrames];
            // Find the ticks per millisecond that defines how fast the animation should go
            double ticksPerMS = animTicksPerSecond == null ? DEFAULT_TICKS_PER_SECOND / 1000.0
                : (double)animTicksPerSecond / 1000.0;

            // fill in the frames
            for (int i = 0; i < numFrames; i++)
            {
                // Create a timespan object that represents the time that the current keyframe
                // occurs
                TimeSpan time = new TimeSpan(0, 0, 0, 0,
                                             (int)(tokens.NextInt() / ticksPerMS));
                // The number of keys that represents the transform for this keyframe.
                // Quaternions (rotation keys) have 4,
                // Vectors (scale and translation) have 3,
                // Matrices have 16
                int    numKeys   = tokens.NextInt();
                Matrix transform = new Matrix();
                if (numKeys == 16)
                {
                    transform = tokens.NextMatrix();
                }
                else if (numKeys == 4)
                {
                    Vector4    v = tokens.NextVector4();
                    Quaternion q = new Quaternion(
                        new Vector3(-v.Y, -v.Z, -v.W),
                        v.X);


                    transform = Matrix.CreateFromQuaternion(q);
                }
                else if (numKeys == 3)
                {
                    Vector3 v = tokens.NextVector3();

                    if (keyType == 1)
                    {
                        Matrix.CreateScale(ref v, out transform);
                    }
                    else
                    {
                        Matrix.CreateTranslation(ref v, out transform);
                    }
                }
                tokens.SkipToken();
                frames[i] = new AnimationKeyframe(time, transform);
            }
            tokens.SkipToken();
            return(frames);
        }
示例#4
0
        private void listKeyframes_SelectedIndexChanged(object sender, EventArgs e)
        {
            int index = listKeyframes.SelectedIndex;

            if (index >= 0)
            {
                AnimationKeyframe f = (AnimationKeyframe)listKeyframes.SelectedItem;
                numFrame.Value = f.Index + 1;
            }
        }
示例#5
0
 private static void InitializeFrames(ref AnimationKeyframe[] frames)
 {
     SortFrames(ref frames);
     if (frames[0].Time != TimeSpan.Zero)
     {
         AnimationKeyframe[] newFrames = new AnimationKeyframe[frames.Length + 1];
         Array.ConstrainedCopy(frames, 0, newFrames, 1, frames.Length);
         newFrames[0] = frames[0];
         frames       = newFrames;
     }
 }
示例#6
0
        /// <summary>
        /// Create an AvatarRenderer-friendly matrix from an animation keyframe.
        /// </summary>
        /// <param name="keyframe">The keyframe to be converted.</param>
        /// <param name="boneIndex">The index of the bone this keyframe is for.</param>
        /// <returns>The converted AvatarRenderer-friendly matrix for this bone and keyframe.</returns>
        private Matrix CreateKeyFrameMatrix(AnimationKeyframe keyframe, int boneIndex)
        {
            // safety-check the parameter
            if (keyframe == null)
            {
                throw new ArgumentNullException("keyframe");
            }

            // Retrieve the transform for this keyframe
            Matrix keyframeMatrix;

            // The root node is transformed by the root of the bind pose
            // We need to make the keyframe relative to the root
            if (boneIndex == 0)
            {
                // When the animation is exported the bind pose can have the
                // wrong translation of the root node so we hard code it here
                Vector3 bindPoseTranslation = new Vector3(0.000f, 75.5199f, -0.8664f);

                Matrix keyTransfrom = keyframe.Transform;

                Matrix inverseBindPose = _bindPoses[boneIndex];
                inverseBindPose.Translation -= bindPoseTranslation;
                inverseBindPose              = Matrix.Invert(inverseBindPose);

                keyframeMatrix              = (keyTransfrom * inverseBindPose);
                keyframeMatrix.Translation -= bindPoseTranslation;

                // Scale from cm to meters
                keyframeMatrix.Translation *= 0.01f;
            }
            else
            {
                keyframeMatrix = keyframe.Transform;
                // Only the root node can have translation
                keyframeMatrix.Translation = Vector3.Zero;
            }

            return(keyframeMatrix);
        }
        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);
        }
示例#8
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);
        }
示例#9
0
        /// <summary>
        /// Merges scale, translation, and rotation keyframes into matrix keyframes.
        /// </summary>
        /// <param name="scale">The scale keyframes.</param>
        /// <param name="translation">The translation keyframes.</param>
        /// <param name="rotation">The rotation keyframes.</param>
        /// <returns>The merged matrix keyframes.</returns>
        public static List <AnimationKeyframe> MergeKeyFrames(AnimationKeyframe[] scale,
                                                              AnimationKeyframe[] translation, AnimationKeyframe[] rotation)
        {
            if (scale == null)
            {
                scale = new AnimationKeyframe[] { new AnimationKeyframe(new TimeSpan(0),
                                                                        Matrix.Identity) };
            }
            if (translation == null)
            {
                throw new Exception("Animation data is not stored as matrices and " +
                                    "has no translation component.");
            }
            if (rotation == null)
            {
                throw new Exception("Animation data is not stored as matrices and " +
                                    "has no rotation component");
            }

            // Sort the frames by time and make sure they start at time 0 and
            // have length >= 1
            InitializeFrames(ref scale);
            InitializeFrames(ref translation);
            InitializeFrames(ref rotation);

            // Get a sorted list of the timespans for all 3 keyframe types,
            // not counting duplicates
            SortedList <TimeSpan, object> keyframeTimes
                = new SortedList <TimeSpan, object>();

            foreach (AnimationKeyframe frame in scale)
            {
                if (!keyframeTimes.ContainsKey(frame.Time))
                {
                    keyframeTimes.Add(frame.Time, null);
                }
            }
            foreach (AnimationKeyframe frame in translation)
            {
                if (!keyframeTimes.ContainsKey(frame.Time))
                {
                    keyframeTimes.Add(frame.Time, null);
                }
            }
            foreach (AnimationKeyframe frame in rotation)
            {
                if (!keyframeTimes.ContainsKey(frame.Time))
                {
                    keyframeTimes.Add(frame.Time, null);
                }
            }


            IList <TimeSpan> times = keyframeTimes.Keys;

            // Allocate the interpolated frame matrices
            Matrix[] newScales = new Matrix[keyframeTimes.Count];
            Matrix[] newTrans  = new Matrix[keyframeTimes.Count];
            Matrix[] newRot    = new Matrix[keyframeTimes.Count];
            List <AnimationKeyframe> returnFrames = new List <AnimationKeyframe>();

            // Interpolate the frames based on the times
            InterpFrames(ref scale, ref newScales, times);
            InterpFrames(ref translation, ref newTrans, times);
            InterpFrames(ref rotation, ref newRot, times);

            // Merge the 3 keyframe types into one.
            for (int i = 0; i < times.Count; i++)
            {
                Matrix m = newRot[i];
                m = m * newTrans[i];
                m = newScales[i] * m;
                returnFrames.Add(new AnimationKeyframe(times[i], m));
            }

            return(returnFrames);
        }
示例#10
0
        /// <summary>
        /// Imports BVH (Biovision hierarchical) animation data.
        /// Stores animation data in root bone.
        /// </summary>
        public override BoneContent Import(string filename, ContentImporterContext context)
        {
            this.context = context;
            contentId    = new ContentIdentity(filename, GetType().ToString());

            AnimationContent animation = new AnimationContent();

            animation.Name     = Path.GetFileNameWithoutExtension(filename);
            animation.Identity = contentId;

            bones  = new List <BoneInfo>();
            reader = new StreamReader(filename);
            string line;

            if ((line = readLine()) != "HIERARCHY")
            {
                throw new InvalidContentException("no HIERARCHY found", cId);
            }
            BoneContent bone = root;

            while ((line = readLine()) != "MOTION")
            {
                if (line == null)
                {
                    throw new InvalidContentException("premature end of file", cId);
                }
                string keyword = line.Split(whiteSpace)[0];
                if (keyword == "ROOT" || keyword == "JOINT" || line == "End Site")
                {
                    BoneInfo    boneInfo = new BoneInfo();
                    BoneContent newBone  = new BoneContent();
                    if (keyword == "JOINT" || line == "End Site")
                    {
                        bone.Children.Add(newBone);
                    }
                    if (keyword == "ROOT")
                    {
                        root = newBone;
                    }
                    if (keyword == "ROOT" || keyword == "JOINT")
                    {
                        newBone.Name  = line.Split(whiteSpace)[1];
                        boneInfo.bone = newBone;
                        bones.Add(boneInfo);
                    }
                    else
                    {
                        newBone.Name = bone.Name + "End";
                    }
                    if ((line = readLine()) != "{")
                    {
                        throw new InvalidContentException("expected '{' but found " + line, cId);
                    }
                    line = readLine();
                    if (line != null && line.StartsWith("OFFSET"))
                    {
                        string[] data = line.Split(whiteSpace);
                        //couldn't get the .NET 2.0 version of Split() working,
                        //therefore this ugly hack
                        List <string> coords = new List <string>();
                        foreach (string s in data)
                        {
                            if (s != "OFFSET" && s != "" && s != null)
                            {
                                coords.Add(s);
                            }
                        }
                        Vector3 v = new Vector3();
                        v.X = ParseFloat(coords[0]);
                        v.Y = ParseFloat(coords[1]);
                        v.Z = ParseFloat(coords[2]);
                        Matrix offset = Matrix.CreateTranslation(v);
                        newBone.Transform = offset;
                    }
                    else
                    {
                        throw new InvalidContentException("expected 'OFFSET' but found " + line, cId);
                    }
                    if (keyword == "ROOT" || keyword == "JOINT")
                    {
                        line = readLine();
                        if (line != null && line.StartsWith("CHANNELS"))
                        {
                            string[] channels = line.Split(whiteSpace);
                            for (int i = 2; i < channels.Length; i++)
                            {
                                if (channels[i] != null && channels[i] != "")
                                {
                                    boneInfo.Add(channels[i]);
                                }
                            }
                        }
                        else
                        {
                            throw new InvalidContentException("expected 'CHANNELS' but found " + line, cId);
                        }
                    }
                    bone = newBone;
                }
                if (line == "}")
                {
                    bone = (BoneContent)bone.Parent;
                }
            }
            if ((line = readLine()) != null)
            {
                string[] data = line.Split(':');
                if (data[0] == "Frames")
                {
                    frames = int.Parse(data[1].Trim());
                }
            }
            if ((line = readLine()) != null)
            {
                string[] data = line.Split(':');
                if (data[0] == "Frame Time")
                {
                    frameTime = double.Parse(data[1].Trim(), CultureInfo.InvariantCulture);
                }
            }
            animation.Duration = TimeSpan.FromSeconds(frameTime * frames);
            root.Animations.Add(animation.Name, animation);
            foreach (BoneInfo b in bones)
            {
                animation.Channels.Add(b.bone.Name, new AnimationChannel());
            }
            int frameNumber = 0;

            while ((line = readLine()) != null)
            {
                string[] ss = line.Split(whiteSpace);
                //couldn't get the .NET 2.0 version of Split() working,
                //therefore this ugly hack
                List <string> data = new List <string>();
                foreach (string s in ss)
                {
                    if (s != "" && s != null)
                    {
                        data.Add(s);
                    }
                }
                int i = 0;
                foreach (BoneInfo b in bones)
                {
                    foreach (string channel in b.channels)
                    {
                        b.channelValues[channel] = ParseFloat(data[i]);
                        ++i;
                    }
                }
                foreach (BoneInfo b in bones)
                {
                    // Many applications export BVH in such a way that bone translation
                    // needs to be aplied in every frame.
                    Matrix  translation = b.bone.Transform;
                    Vector3 t           = new Vector3();
                    t.X = b["Xposition"];
                    t.Y = b["Yposition"];
                    t.Z = b["Zposition"];
                    if (t.Length() != 0.0f)
                    {
                        // Some applications export BVH with translation channels for every bone.
                        // In this case, bone translation should not be applied.
                        translation = Matrix.CreateTranslation(t);
                    }
                    Quaternion r = Quaternion.Identity;
                    // get rotations in correct order
                    foreach (string channel in b.channels)
                    {
                        float angle = MathHelper.ToRadians(b[channel]);
                        if (channel.Equals("Xrotation", StringComparison.InvariantCultureIgnoreCase))
                        {
                            r = r * Quaternion.CreateFromAxisAngle(Vector3.UnitX, angle);
                        }
                        if (channel.Equals("Yrotation", StringComparison.InvariantCultureIgnoreCase))
                        {
                            r = r * Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle);
                        }
                        if (channel.Equals("Zrotation", StringComparison.InvariantCultureIgnoreCase))
                        {
                            r = r * Quaternion.CreateFromAxisAngle(Vector3.UnitZ, angle);
                        }
                    }
                    Matrix            m        = Matrix.CreateFromQuaternion(r) * translation;
                    TimeSpan          time     = TimeSpan.FromSeconds(frameTime * frameNumber);
                    AnimationKeyframe keyframe = new AnimationKeyframe(time, m);
                    animation.Channels[b.bone.Name].Add(keyframe);
                    ++i;
                }
                ++frameNumber;
            }
            root.Identity = contentId;
            return(root);
        }
 private static void SortFrames(ref AnimationKeyframe[] frames)
 {
     Array.Sort<AnimationKeyframe>(frames, new Comparison<AnimationKeyframe>(
         delegate(AnimationKeyframe one, AnimationKeyframe two)
         {
             return one.Time.CompareTo(two.Time);
         }));
 }
示例#12
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 static void InitializeFrames(ref AnimationKeyframe[] frames)
 {
     SortFrames(ref frames);
     if (frames[0].Time != TimeSpan.Zero)
     {
         AnimationKeyframe[] newFrames = new AnimationKeyframe[frames.Length + 1];
         Array.ConstrainedCopy(frames, 0, newFrames, 1, frames.Length);
         newFrames[0] = frames[0];
         frames = newFrames;
     }
 }
        // Interpolates a set of animation key frames to align with
        // a set of times and copies it into the destination array.
        private static void InterpFrames(
            ref AnimationKeyframe[] source,
            ref Matrix[] dest,
            IList<TimeSpan> times)
        {
            // The index of hte source frame
            int sourceIndex = 0;
            for (int i = 0; i < times.Count; i++)
            {
                // Increment the index till the next index is greater than the current time
                while (sourceIndex != source.Length-1 && source[sourceIndex + 1].Time < times[i])
                {
                    sourceIndex++;
                }
                // If we are at the last index use the last transform for the rest of the times
                if (sourceIndex==source.Length-1)
                {
                    dest[i] = source[sourceIndex].Transform;
                    continue;
                }
                // If the keyframe time is equal to the current time use the keyframe transform
                if (source[sourceIndex].Time == times[i])
                {
                    dest[i] = source[sourceIndex].Transform;
                }
                else // else interpolate
                {
                    double interpAmount = ((double)times[i].Ticks - source[sourceIndex].Time.Ticks) /
                        ((double)source[sourceIndex + 1].Time.Ticks - source[sourceIndex].Time.Ticks);
                    Matrix m1 = source[sourceIndex].Transform;
                    Matrix m2 = source[sourceIndex + 1].Transform;
                    if (m1 == m2)
                        dest[i] = m1;
                    else
                        dest[i] = Matrix.Lerp(m1, m2, (float)interpAmount);

                }

            }
        }
        /// <summary>
        /// Merges scale, translation, and rotation keyframes into matrix keyframes.
        /// </summary>
        /// <param name="scale">The scale keyframes.</param>
        /// <param name="translation">The translation keyframes.</param>
        /// <param name="rotation">The rotation keyframes.</param>
        /// <returns>The merged matrix keyframes.</returns>
        public static List<AnimationKeyframe> MergeKeyFrames(AnimationKeyframe[] scale,
            AnimationKeyframe[] translation, AnimationKeyframe[] rotation)
        {
            if (scale == null)
            {
                scale = new AnimationKeyframe[] {new AnimationKeyframe(new TimeSpan(0),
                    Matrix.Identity)};
            }
            if (translation == null)
                throw new Exception("Animation data is not stored as matrices and " +
                    "has no translation component.");
            if (rotation == null)
                throw new Exception("Animation data is not stored as matrices and " +
                    "has no rotation component");

            // Sort the frames by time and make sure they start at time 0 and
            // have length >= 1
            InitializeFrames(ref scale);
            InitializeFrames(ref translation);
            InitializeFrames(ref rotation);

            // Get a sorted list of the timespans for all 3 keyframe types,
            // not counting duplicates
            SortedList<TimeSpan, object> keyframeTimes
                = new SortedList<TimeSpan, object>();
            foreach (AnimationKeyframe frame in scale)
                if (!keyframeTimes.ContainsKey(frame.Time))
                    keyframeTimes.Add(frame.Time, null);
            foreach (AnimationKeyframe frame in translation)
                if (!keyframeTimes.ContainsKey(frame.Time))
                    keyframeTimes.Add(frame.Time, null);
            foreach (AnimationKeyframe frame in rotation)
                if (!keyframeTimes.ContainsKey(frame.Time))
                    keyframeTimes.Add(frame.Time, null);

            IList<TimeSpan> times = keyframeTimes.Keys;

            // Allocate the interpolated frame matrices
            Matrix[] newScales = new Matrix[keyframeTimes.Count];
            Matrix[] newTrans = new Matrix[keyframeTimes.Count];
            Matrix[] newRot = new Matrix[keyframeTimes.Count];
            List<AnimationKeyframe> returnFrames = new List<AnimationKeyframe>();

            // Interpolate the frames based on the times
            InterpFrames(ref scale, ref newScales, times);
            InterpFrames(ref translation, ref newTrans, times);
            InterpFrames(ref rotation, ref newRot, times);

            // Merge the 3 keyframe types into one.
            for (int i = 0; i < times.Count; i++)
            {
                Matrix m = newRot[i];
                m = m * newTrans[i];
                m = newScales[i] * m;
                returnFrames.Add(new AnimationKeyframe(times[i], m));
            }

            return returnFrames;
        }
示例#16
0
        /// <summary>
        ///  Imports a keyframe
        /// </summary>
        private AnimationKeyframe importKeyframe(string[] s, int frameNumber)
        {
            AnimationKeyframe keyframe = null;

            if (!bones.ContainsKey(s[0]))
            {
                throw new InvalidContentException("skeleton does not have bone " + s[0], cId);
            }
            BoneContent bone = bones[s[0]];

            if (bone.OpaqueData.ContainsKey("dof"))
            {
                string[] dof        = (string[])bone.OpaqueData["dof"];
                int      dataLength = s.Length - 1;
                if (dataLength != dof.Length)
                {
                    throw new InvalidContentException("AFS DOF specifies " + dof.Length
                                                      + " values but AMC has " + dataLength, cId);
                }
                Matrix     transform = Matrix.Identity;
                Vector3    t         = new Vector3();
                Quaternion r         = Quaternion.Identity;
                for (int i = 0; i < dataLength; i++)
                {
                    float data = float.Parse(s[i + 1],
                                             NumberStyles.Float,
                                             CultureInfo.InvariantCulture.NumberFormat);
                    if (dof[i] == "tx")
                    {
                        t.X = data;
                    }
                    else if (dof[i] == "ty")
                    {
                        t.Y = data;
                    }
                    else if (dof[i] == "tz")
                    {
                        t.Z = data;
                    }
                    else if (dof[i] == "rx")
                    {
                        r = r * Quaternion.CreateFromAxisAngle(Vector3.UnitX, MathHelper.ToRadians(data));
                    }
                    else if (dof[i] == "ry")
                    {
                        r = r * Quaternion.CreateFromAxisAngle(Vector3.UnitY, MathHelper.ToRadians(data));
                    }
                    else if (dof[i] == "rz")
                    {
                        r = r * Quaternion.CreateFromAxisAngle(Vector3.UnitZ, MathHelper.ToRadians(data));
                    }
                }
                if (t.Length() == 0)
                {
                    t = bone.Transform.Translation;
                }
                transform = Matrix.CreateFromQuaternion(r) * Matrix.CreateTranslation(t);
                TimeSpan time = TimeSpan.FromTicks(frameNumber * ticksPerFrame);
                keyframe = new AnimationKeyframe(time, transform);
            }
            return(keyframe);
        }
        private static AnimationClip ProcessAnimation(AnimationContent animation, Dictionary <string, int> boneMap)
        {
            int             num = 0;
            bool            count;
            List <Keyframe> keyframes = new List <Keyframe>();
            IEnumerator <KeyValuePair <string, AnimationChannel> > enumerator = animation.Channels.GetEnumerator();

            try {
                while (true)
                {
                    count = enumerator.MoveNext();
                    if (!count)
                    {
                        break;
                    }
                    KeyValuePair <string, AnimationChannel> current = enumerator.Current;
                    count = boneMap.TryGetValue(current.Key, out num);
                    if (!count)
                    {
                        throw new InvalidContentException(string.Format("Found animation for bone '{0}', which is not part of the skeleton.", current.Key));
                    }
                    IEnumerator <AnimationKeyframe> enumerator1 = current.Value.GetEnumerator();
                    try {
                        while (true)
                        {
                            count = enumerator1.MoveNext();
                            if (!count)
                            {
                                break;
                            }
                            AnimationKeyframe animationKeyframe = enumerator1.Current;
                            keyframes.Add(new Keyframe(num, animationKeyframe.Time, animationKeyframe.Transform));
                        }
                    } finally {
                        count = enumerator1 == null;
                        if (!count)
                        {
                            enumerator1.Dispose();
                        }
                    }
                }
            } finally {
                count = enumerator == null;
                if (!count)
                {
                    enumerator.Dispose();
                }
            }
            keyframes.Sort(SkinnedModelProcessor.CompareKeyframeTimes);
            count = keyframes.Count != 0;
            if (count)
            {
                count = !(animation.Duration <= TimeSpan.Zero);
                if (count)
                {
                    AnimationClip animationClip = new AnimationClip(animation.Duration, keyframes);
                    return(animationClip);
                }
                else
                {
                    throw new InvalidContentException("Animation has a zero duration.");
                }
            }
            else
            {
                throw new InvalidContentException("Animation has no keyframes.");
            }
        }
示例#18
0
        /// <summary>
        /// Imports Acclaim AMC (motion capture data).
        /// For a foo_bar.amc, expects skeleton in a file named foo.asf.
        /// Returns a skeleton with Animations in root bone.
        /// </summary>
        public override BoneContent Import(string filename, ContentImporterContext context)
        {
            this.context = context;
            contentId    = new ContentIdentity(filename, GetType().ToString());
            reader       = new StreamReader(filename);

            AnimationContent animation = new AnimationContent();

            animation.Name     = Path.GetFileNameWithoutExtension(filename);
            animation.Identity = contentId;

            string dir         = Path.GetDirectoryName(filename);
            string asfFilename = animation.Name + ".asf";

            if (animation.Name.Contains("_"))
            {
                //asfFilename = animation.Name.Split('_')[0] + ".asf";
            }
            asfFilename = dir + @"\" + asfFilename;
            context.Logger.LogWarning("", contentId, "using skeleton from {0}", asfFilename);
            AsfImporter asfImporter = new AsfImporter();
            BoneContent root        = asfImporter.Import(asfFilename, context);

            bones = asfImporter.Bones;

            int    frameNumber    = 1;
            int    maxFrameNumber = 0;
            string line;

            animation.Channels.Add("root", new AnimationChannel());
            while ((line = readLine()) != null)
            {
                if (line[0] != '#' && line[0] != ':')
                {
                    int fn = 0;
                    if (int.TryParse(line, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out fn))
                    {
                        ++frameNumber;// = int.Parse(line, NumberStyles.Int, CultureInfo.InvariantCulture.NumberFormat);
                        maxFrameNumber = Math.Max(frameNumber, maxFrameNumber);
                        TimeSpan time = TimeSpan.FromTicks(frameNumber * ticksPerFrame);
                        animation.Channels["root"].Add(new AnimationKeyframe(time, Matrix.Identity));
                    }
                    else
                    {
                        string[] s    = line.Split(' ');
                        string   bone = s[0];
                        if (!animation.Channels.ContainsKey(bone))
                        {
                            animation.Channels.Add(bone, new AnimationChannel());
                        }
                        AnimationChannel  channel  = animation.Channels[bone];
                        AnimationKeyframe keyframe = importKeyframe(s, frameNumber);
                        if (keyframe != null)
                        {
                            channel.Add(keyframe);
                        }
                    }
                }
            }
            animation.Duration = TimeSpan.FromTicks(maxFrameNumber * ticksPerFrame);
            context.Logger.LogImportantMessage("imported {0} animation frames for {1} bones",
                                               maxFrameNumber, animation.Channels.Count);
            root.Animations.Add(animation.Name, animation);
            return(root);
        }
        /// <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>
        /// 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);
        }
示例#21
0
        private static AnimationChannel BuildAnimtionChannel(Node animatedNode, NodeAnimationChannel nac)
        {
            AnimationChannel newChan = new AnimationChannel();

            Matrix animatedNodeTransform = Matrix.Transpose(ToXna(animatedNode.Transform));

            Vector3 xnaScale;

            Microsoft.Xna.Framework.Quaternion xnaRotation;
            Vector3 xnaPositon;

            animatedNodeTransform.Decompose(out xnaScale, out xnaRotation, out xnaPositon);

            Vector3D scale = new Vector3D(xnaScale.X, xnaScale.Y, xnaScale.Z);

            Assimp.Quaternion rotation;
            rotation.W = xnaRotation.W;
            rotation.X = xnaRotation.X;
            rotation.Y = xnaRotation.Y;
            rotation.Z = xnaRotation.Z;
            Vector3D position = new Vector3D(xnaPositon.X, xnaPositon.Y, xnaPositon.Z);

            int sKeyIndex = 0;
            int qKeyIndex = 0;
            int tKeyIndex = 0;

            double firstSKeyTime = nac.ScalingKeyCount > sKeyIndex ? nac.ScalingKeys[sKeyIndex].Time : Double.MaxValue;
            double firstQKeyTime = nac.RotationKeyCount > qKeyIndex ? nac.RotationKeys[qKeyIndex].Time : Double.MaxValue;
            double firstTKeyTime = nac.PositionKeyCount > tKeyIndex ? nac.PositionKeys[tKeyIndex].Time : Double.MaxValue;

            double currTime = Math.Min(Math.Min(firstSKeyTime, firstQKeyTime), firstTKeyTime);

            if (firstSKeyTime <= currTime)
            {
                scale = nac.ScalingKeys[sKeyIndex].Value;
            }

            if (firstQKeyTime <= currTime)
            {
                rotation = nac.RotationKeys[qKeyIndex].Value;
            }

            if (firstTKeyTime <= currTime)
            {
                position = nac.PositionKeys[tKeyIndex].Value;
            }

            while (currTime < double.MaxValue)
            {
                while (nac.ScalingKeyCount > sKeyIndex + 1 &&
                       nac.ScalingKeys[sKeyIndex + 1].Time <= currTime)
                {
                    sKeyIndex++;
                    scale = nac.ScalingKeys[sKeyIndex].Value;
                }

                while (nac.RotationKeyCount > qKeyIndex + 1 &&
                       nac.RotationKeys[qKeyIndex + 1].Time <= currTime)
                {
                    qKeyIndex++;
                    rotation = nac.RotationKeys[qKeyIndex].Value;
                }

                while (nac.PositionKeyCount > tKeyIndex + 1 &&
                       nac.PositionKeys[tKeyIndex + 1].Time <= currTime)
                {
                    tKeyIndex++;
                    position = nac.PositionKeys[tKeyIndex].Value;
                }

                xnaRotation.W = rotation.W;
                xnaRotation.X = rotation.X;
                xnaRotation.Y = rotation.Y;
                xnaRotation.Z = rotation.Z;

                Matrix transform =
                    Matrix.CreateScale(ToXna(scale)) *
                    Matrix.CreateFromQuaternion(xnaRotation) *
                    Matrix.CreateTranslation(ToXna(position));

                AnimationKeyframe newKeyframe = new AnimationKeyframe(TimeSpan.FromSeconds(currTime), transform);
                newChan.Add(newKeyframe);

                // Increment the time:

                double nextSKeyTime = nac.ScalingKeyCount > sKeyIndex + 1 ? nac.ScalingKeys[sKeyIndex + 1].Time : Double.MaxValue;
                double nextQKeyTime = nac.RotationKeyCount > qKeyIndex + 1 ? nac.RotationKeys[qKeyIndex + 1].Time : Double.MaxValue;
                double nextTKeyTime = nac.PositionKeyCount > tKeyIndex + 1 ? nac.PositionKeys[tKeyIndex + 1].Time : Double.MaxValue;

                currTime = Math.Min(Math.Min(nextSKeyTime, nextQKeyTime), nextTKeyTime);
            }

            return(newChan);
        }