/// <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);
        }
Пример #2
0
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContent
        /// object to our runtime AnimationClip format.
        /// </summary>
        static Clip ProcessAnimation(AnimationContent animation, Dictionary<string, int> boneMap)
        {
            Dictionary<int, Channel> channels = new Dictionary<int, Channel>();

            // For each input animation channel.
            foreach (KeyValuePair<string, AnimationChannel> inputChannel in animation.Channels)
            {
                // Look up what bone this channel is controlling.
                int boneIndex;

                if (!boneMap.TryGetValue(inputChannel.Key, out boneIndex))
                {
                    throw new InvalidContentException(string.Format(
                        "Found animation for bone '{0}', " +
                        "which is not part of the skeleton.", inputChannel.Key));
                }

                if (inputChannel.Value.Count > 0)
                {
                    Channel newChannel = null;

                    if (!channels.TryGetValue(boneIndex, out newChannel))
                    {
                        channels[boneIndex] = newChannel = new Channel();
                        newChannel.BoneIndex = boneIndex;
                    }

                    foreach (AnimationKeyframe keyframe in inputChannel.Value)
                    {
                        if (newChannel.Count == 0 || !newChannel[newChannel.Count - 1].Transform.Equals(keyframe.Transform))
                            newChannel.Add(new Keyframe(new TimeSpan(keyframe.Time.Ticks), keyframe.Transform));
                    }
                    newChannel.Sort(CompareKeyframeTimes);
                }
            }

            return new Clip(new TimeSpan(animation.Duration.Ticks), channels.Values);
        }
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContent
        /// object to our runtime AnimationClip format.
        /// </summary>
        static AnimationClip ProcessAnimation(AnimationContent animation,
                                              Dictionary<string, int> boneMap, string clipName)
        {
            List<Keyframe> keyframes = new List<Keyframe>();

            // For each input animation channel.
            foreach (KeyValuePair<string, AnimationChannel> channel in
                animation.Channels)
            {
                // Look up what bone this channel is controlling.
                int boneIndex = boneMap[channel.Key];

                // Convert the keyframe data.
                foreach (AnimationKeyframe keyframe in channel.Value)
                {
                    keyframes.Add(new Keyframe(boneIndex, keyframe.Time,
                                               keyframe.Transform));
                }
            }
            // Sort the merged keyframes by time.
            keyframes.Sort(CompareKeyframeTimes);

            return new AnimationClip(animation.Duration, keyframes,  clipName);
        }
Пример #4
0
		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.");
			}
		}
Пример #5
0
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContent
        /// object to our runtime AnimationClip format.
        /// </summary>
        static AnimationClip ProcessAnimation(AnimationContent animation,
            Dictionary<string, int> boneMap)
        {
            //System.Diagnostics.Debugger.Launch();
            List<Keyframe> keyframes = new List<Keyframe>();

            int affectedBones = animation.Channels.Count;
            int[] affectedBoneIndices = new int[affectedBones];

            int affectedBoneIndex = 0;
            // For each input animation channel.
            foreach (KeyValuePair<string, AnimationChannel> channel in
                animation.Channels)
            {
                // Look up what bone this channel is controlling.
                int boneIndex;

                if (!boneMap.TryGetValue(channel.Key, out boneIndex))
                {
                    throw new InvalidContentException(string.Format(
                        "Found animation for bone '{0}', " +
                        "which is not part of the skeleton.", channel.Key));
                }
                // save the index of the affected bone
                affectedBoneIndices[affectedBoneIndex++] = boneIndex;

                // Convert the keyframe data.
                foreach (AnimationKeyframe keyframe in channel.Value)
                {
                    keyframes.Add(new Keyframe(boneIndex, 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.");

            AnimationClip clip = new AnimationClip(animation.Duration, keyframes, affectedBoneIndices);
            return clip;
        }
        /// <summary>
        /// Converts an AnimationContent to a SkeletonKeyFrameAnimation.
        /// </summary>
        private SkeletonKeyFrameAnimation ProcessAnimation(AnimationContent animationContent, Skeleton skeleton, ContentProcessorContext context)
        {
            var animation = new SkeletonKeyFrameAnimation { EnableInterpolation = true };

              // Process all animation channels (each channel animates a bone).
              int numberOfKeyFrames = 0;
              foreach (var item in animationContent.Channels)
              {
            string channelName = item.Key;
            AnimationChannel channel = item.Value;

            int boneIndex = skeleton.GetIndex(channelName);
            if (boneIndex == -1)
            {
              var message = string.Format("Found animation for bone '{0}', which is not part of the skeleton.", channelName);
              throw new InvalidContentException(message, animationContent.Identity);
            }

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

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

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

              animation.AddKeyFrame(boneIndex, time, transform);
              numberOfKeyFrames++;
            }
              }

              if (numberOfKeyFrames == 0)
            throw new InvalidContentException("Animation has no keyframes.", animationContent.Identity);

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

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

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

              return animation;
        }
        private SkeletonKeyFrameAnimation BuildAnimation(AnimationContent animationContent)
        {
            string name = animationContent.Name;

              // Add loop frame?
              bool addLoopFrame = false;
              if (_modelDescription != null)
              {
            var animationDescription = _modelDescription.Animation;
            if (animationDescription != null)
            {
              addLoopFrame = animationDescription.AddLoopFrame ?? false;

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

                break;
              }
            }
              }
            }
              }

              var animation = new SkeletonKeyFrameAnimation { EnableInterpolation = true };

              // Process all animation channels (each channel animates a bone).
              int numberOfKeyFrames = 0;
              foreach (var item in animationContent.Channels)
              {
            string channelName = item.Key;
            AnimationChannel channel = item.Value;

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

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

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

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

            if (loopFrame == null)
              loopFrame = transform;

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

            numberOfKeyFrames++;
              }

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

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

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

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

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

              return animation;
        }
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContent
        /// object to our runtime AnimationClip format.
        /// </summary>
        public static AnimationClip ProcessAnimation(AnimationContent animation,
            IDictionary<string, int> boneMap,
            ContentProcessorContext context)
        {
            List<Keyframe> keyframes = new List<Keyframe>();

            // For each input animation channel.
            foreach (KeyValuePair<string, AnimationChannel> channel in
                animation.Channels)
            {
                // Look up what bone this channel is controlling.
                int boneIndex;

                if (!boneMap.TryGetValue(channel.Key, out boneIndex))
                {
                    //throw new InvalidContentException(string.Format(
                      //  "Found animation for bone '{0}', " +
                        //"which is not part of the skeleton.", channel.Key));
                    context.Logger.LogWarning(null, null,
                        string.Format("Found animation for bone '{0}', " +
                        "which is not part of the skeleton.", channel.Key));
                    // Skip this channel
                    continue;
                }

                // Convert the keyframe data.
                foreach (AnimationKeyframe keyframe in channel.Value)
                {
                    keyframes.Add(new Keyframe(boneIndex, keyframe.Time,
                                               keyframe.Transform));
                }
            }

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

            if (keyframes.Count == 0)
            {
                //throw new InvalidContentException("Animation has no keyframes.");
                context.Logger.LogWarning(null, null, "Animation has no keyframes.");
                return null;
            }

            if (animation.Duration <= TimeSpan.Zero)
            {
                //throw new InvalidContentException("Animation has a zero duration.");
                context.Logger.LogWarning(null, null, "Animation has a zero duration.");
                return null;
            }

            // A model from a 3D modelling package does not have sound cues
            // We have to add them manually to our modified clip file
            List<TimeSpan> dummy = new List<TimeSpan>();

            return new AnimationClip(boneMap.Count, animation.Duration, keyframes, dummy);
        }
Пример #9
0
 /// <summary>
 /// Return true if you want to trim the given animation, removing identical frames 
 /// from the end. This is necessay for short animations imported through the .X 
 /// importer, which somehow manages to add blank padding to the duration.
 /// </summary>
 /// <param name="ac">The animation to test against the list of animation name patterns</param>
 /// <returns></returns>
 protected virtual bool ShouldTrimAnimation(AnimationContent ac)
 {
     if (trimAnimationExpressions_ != null)
     foreach (Regex re in trimAnimationExpressions_)
       if (re.IsMatch(ac.Name))
     return true;
       return false;
 }
Пример #10
0
 /// <summary>
 /// Return true if you want to include the specific animation in the output animation set.
 /// </summary>
 /// <param name="ac">The animation to check.</param>
 /// <returns>true unless the animation name is in a semicolon-separated list called ExcludeAnimations</returns>
 protected virtual bool IncludeAnimation(AnimationContent ac)
 {
     if (excludeAnimationsExpressions_ != null)
     foreach (Regex re in excludeAnimationsExpressions_)
       if (re.IsMatch(ac.Name))
     return false;
       return true;
 }
Пример #11
0
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContent
        /// object to our runtime AnimationClip format.
        /// </summary>
        static Clip ProcessAnimation(AnimationContent animation, Dictionary<string, int> boneMap)
        {
            Dictionary<int, Channel> channels = new Dictionary<int, Channel>();

            // For each input animation channel.
            foreach (KeyValuePair<string, AnimationChannel> inputChannel in animation.Channels)
            {
                // Look up what bone this channel is controlling.
                int boneIndex;

                if (!boneMap.TryGetValue(inputChannel.Key, out boneIndex))
                    continue;

                if (inputChannel.Value.Count > 0)
                {
                    Channel newChannel = null;

                    if (!channels.TryGetValue(boneIndex, out newChannel))
                    {
                        channels[boneIndex] = newChannel = new Channel();
                        newChannel.BoneIndex = boneIndex;
                    }

                    foreach (AnimationKeyframe keyframe in inputChannel.Value)
                        newChannel.Add(new Keyframe(keyframe.Time, keyframe.Transform));

                    newChannel.Sort(CompareKeyframeTimes);

                    for (int i = 2; i < newChannel.Count; i++)
                    {
                        Keyframe keyframe = newChannel[i];
                        if (newChannel[i - 1].Transform.Equals(keyframe.Transform)
                            && newChannel[newChannel.Count - 2].Transform.Equals(keyframe.Transform))
                        {
                            newChannel.RemoveAt(i - 1);
                            i--;
                        }
                    }
                }
            }

            return new Clip(channels.Values);
        }
Пример #12
0
 /// <summary>
 /// Return true if you want to include the specific track (bone) in the output animation set.
 /// </summary>
 /// <param name="ac">The animation to check.</param>
 /// <returns>true unless the bone name is in a semicolon-separated list called ExcludeBones</returns>
 protected virtual bool IncludeTrack(AnimationContent ac, string name)
 {
   if (excludeBonesExpressions_ != null)
     foreach (Regex re in excludeBonesExpressions_)
       if (re.IsMatch(name))
         return false;
   return true;
 }
Пример #13
0
        public void DefaultEffectTest()
        {
            NodeContent input;
            {
                input = new NodeContent();

                var mesh = new MeshContent()
                {
                    Name = "Mesh1"
                };
                mesh.Positions.Add(new Vector3(0, 0, 0));
                mesh.Positions.Add(new Vector3(1, 0, 0));
                mesh.Positions.Add(new Vector3(1, 1, 1));

                var geom = new GeometryContent();
                geom.Vertices.Add(0);
                geom.Vertices.Add(1);
                geom.Vertices.Add(2);
                geom.Indices.Add(0);
                geom.Indices.Add(1);
                geom.Indices.Add(2);

                geom.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(0), new[]
                {
                    new Vector2(0,0),
                    new Vector2(1,0),
                    new Vector2(1,1),
                });

                var wieghts = new BoneWeightCollection();
                wieghts.Add(new BoneWeight("bone1", 0.5f));
                geom.Vertices.Channels.Add(VertexChannelNames.Weights(0), new[]
                {
                    wieghts, 
                    wieghts, 
                    wieghts
                });

                mesh.Geometry.Add(geom);
                input.Children.Add(mesh);

                var bone1 = new BoneContent { Name = "bone1", Transform = Matrix.CreateTranslation(0,1,0) };
                input.Children.Add(bone1);

                var anim = new AnimationContent()
                {
                    Name = "anim1",
                    Duration = TimeSpan.Zero
                };
                input.Animations.Add(anim.Name, anim);
            }

            var processorContext = new TestProcessorContext(TargetPlatform.Windows, "dummy.xnb");
            var processor = new ModelProcessor
            {
                DefaultEffect = MaterialProcessorDefaultEffect.SkinnedEffect,                
            };

            var output = processor.Process(input, processorContext);

            // TODO: Not sure why, but XNA always returns a BasicMaterialContent 
            // even when we specify SkinnedEffect as the default.  We need to fix
            // the test first before we can enable the assert here.

            //Assert.IsInstanceOf(typeof(SkinnedMaterialContent), output.Meshes[0].MeshParts[0].Material);
        }
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContent
        /// object to our runtime AnimationClip format.
        /// </summary>
        AnimationClip ProcessAnimation(AnimationContent animation,
                                              Dictionary<string, int> boneMap)
        {
            List<Keyframe> keyframes = new List<Keyframe>();

            // For each input animation channel.
            foreach (KeyValuePair<string, AnimationChannel> channel in
                animation.Channels)
            {
                // Look up what bone this channel is controlling.
                int boneIndex;

                if (!boneMap.TryGetValue(channel.Key, out boneIndex))
                {
                    throw new InvalidContentException(string.Format(
                        "Found animation for bone '{0}', " +
                        "which is not part of the skeleton.", channel.Key));
                }

                // Convert the keyframe data.
                foreach (AnimationKeyframe keyframe in channel.Value)
                {

                    Matrix m = keyframe.Transform;
                    m.Translation = m.Translation * Scale;
                    keyframe.Transform = m;

                    keyframes.Add(new Keyframe(boneIndex, 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 AnimationClip(animation.Duration, keyframes);
        }
Пример #15
0
        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>
 /// Called when an AnimationContent is processed.
 /// </summary>
 /// <param name="animation">The AnimationContent to be processed.</param>
 /// <returns>The processed AnimationContent.</returns>
 protected virtual AnimationContent ProcessAnimation(AnimationContent animation)
 {
     // M * M = F
     //
     AnimationProcessor ap = new AnimationProcessor();
     AnimationContent newAnim = ap.Interpolate(animation);
     newAnim.Name = animation.Name;
     return newAnim;
     ;
 }
Пример #17
0
        /// <summary>
        /// コンテント・パイプライン内の中間フォーマットである
        /// AnimationContentから、ランタイムフォーマットである
        /// AnimationClipフォーマットに変換する
        /// </summary>
        static AnimationClip ProcessAnimation(AnimationContent animation,
                                              Dictionary<string, int> boneMap)
        {
            List<Keyframe> keyframes = new List<Keyframe>();

            // それぞれのアニメーション・チャンネルを処理する
            foreach (KeyValuePair<string, AnimationChannel> channel in
                animation.Channels)
            {
                // このチャンネルはどのボーンのものか?
                int boneIndex;

                if (!boneMap.TryGetValue(channel.Key, out boneIndex))
                {
                    throw new InvalidContentException(string.Format(
                        "スケルトンに属さないボーン'{0}'"+
                        "のアニメーションが見つかりました。", channel.Key));
                }

                // キーフレームの変換
                foreach (AnimationKeyframe keyframe in channel.Value)
                {
                    keyframes.Add(new Keyframe(boneIndex, keyframe.Time,
                                               QuatTransform.FromMatrix(keyframe.Transform)));
                }
            }

            // キーフレームを時間順に並べ替える
            keyframes.Sort(CompareKeyframeTimes);

            if (keyframes.Count == 0)
                throw new InvalidContentException(
                    "キーフレームの無いアニメーションです。");

            if (animation.Duration <= TimeSpan.Zero)
                throw new InvalidContentException(
                    "再生時間が0のアニメーションです。");

            return new AnimationClip(animation.Duration, keyframes);
        }
        /// <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);
                    }
                }

            }
        }
Пример #19
0
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContent
        /// object to our runtime AnimationClip format.
        /// </summary>
        static AnimationClip ProcessAnimation(AnimationContent animation,
                                              Dictionary<string, int> boneMap)
        {
            List<Keyframe> keyframes = new List<Keyframe>();

            // For each input animation channel.
            foreach (KeyValuePair<string, AnimationChannel> channel in
                animation.Channels)
            {
                // Look up what bone this channel is controlling.
                int boneIndex;

                if (!boneMap.TryGetValue(channel.Key, out boneIndex))
                {
                    continue;
                    //throw new InvalidContentException(string.Format(
                    //    "Found animation for bone '{0}', " +
                    //    "which is not part of the skeleton.", channel.Key));
                }

                // Convert the keyframe data.
                foreach (AnimationKeyframe keyframe in channel.Value.Take(channel.Value.Count - 1))
                {
                    keyframes.Add(new Keyframe(boneIndex, keyframe.Time,
                                               keyframe.Transform));
                }
                keyframes.Add(new Keyframe(boneIndex, channel.Value.Last().Time, channel.Value.First().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 AnimationClip(/*animation.Duration*/ keyframes.Max(x => x.Time), keyframes);
        }
Пример #20
0
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContent
        /// object to our runtime AnimationClip format.
        /// </summary>
        static Clip ProcessAnimation(
            AnimationContent animation,
            Dictionary<string, int> boneMap,
            ClipData clipData)
        {
            List<Keyframe> keyframes = new List<Keyframe>();
            Dictionary<TimeSpan, AnimationControlEvents> controlEvents = new Dictionary<TimeSpan,AnimationControlEvents>();

            bool frameTimesFilled = false;
            TimeSpan[] frameTimes = new TimeSpan[clipData.LastFrame - clipData.FirstFrame + 1];
            TimeSpan syncTime = TimeSpan.Zero;

            // For each input animation channel.
            foreach (KeyValuePair<string, AnimationChannel> channel in animation.Channels)
            {
                // Look up what bone this channel is controlling.
                int boneIndex;

                if (!boneMap.TryGetValue(channel.Key, out boneIndex))
                {
                    // string.Format("Found animation for bone '{0}', which is not part of the skeleton.", channel.Key));

                    // We can just ignore these channels.
                    continue;
                }

                if (!frameTimesFilled)
                {
                    // I guess the frames in the ClipData are specified with a 0-based index?
                    TimeSpan startTime = channel.Value[clipData.FirstFrame].Time;

                    for (int i = clipData.FirstFrame; i <= clipData.LastFrame; i++)
                    {
                        frameTimes[i - clipData.FirstFrame] = channel.Value[i].Time - startTime;
                        // Also, let's get the times for the events:
                        foreach (KeyValuePair<int, AnimationControlEvents> ace in clipData.Events)
                        {
                            if (ace.Key == i)
                            {
                                controlEvents.Add(frameTimes[i - clipData.FirstFrame], ace.Value);
                            }
                        }

                        // Don't forget to convert the sync frame offset into a time:
                        if (clipData.SyncFrameOffset == i - clipData.FirstFrame)
                            syncTime = frameTimes[i - clipData.FirstFrame];
                    }

                    if (controlEvents.Count != clipData.Events.Count)
                        throw new InvalidContentException("The time index of some control events could not be determined.");

                    frameTimesFilled = true;
                }

                // Convert the keyframe data.
                for (int i = clipData.FirstFrame; i <= clipData.LastFrame; i++)
                {
                    Matrix boneTransform;
                    if (channel.Value.Count <= i)
                    {
                        boneTransform = channel.Value[channel.Value.Count - 1].Transform;
                    }
                    else
                    {
                        boneTransform = channel.Value[i].Transform;
                    }

                    keyframes.Add(new Keyframe(boneIndex, frameTimes[i - clipData.FirstFrame], boneTransform));
                }

            }

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

            TimeSpan framePeriod = TimeSpan.Zero;
            if (keyframes.Count > 1)
                 framePeriod = keyframes[1].Time - keyframes[0].Time;

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

            TimeSpan duration = keyframes[keyframes.Count - 1].Time + framePeriod;

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

            return new Clip(duration, keyframes, clipData.Loopable, controlEvents, syncTime);
        }