/// <summary>
        /// The main Process method converts an intermediate format content pipeline
        /// NodeContent tree to a ModelContent object with embedded animation data.
        /// </summary>
        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            contentPath = Environment.CurrentDirectory;

            using (XmlReader reader = XmlReader.Create(MaterialDataFilePath))
            {
                incomingMaterials = IntermediateSerializer.Deserialize<List<MaterialData>>(reader, null);
            }
            context.AddDependency(Path.Combine(Environment.CurrentDirectory, MaterialDataFilePath));

            // Chain to the base ModelProcessor class so it can convert the model data.
            ModelContent model = base.Process(input, context);

            // Put the material's flags into the ModelMeshPartContent's Tag property.
            foreach (ModelMeshContent mmc in model.Meshes)
            {
                foreach (ModelMeshPartContent mmpc in mmc.MeshParts)
                {
                    MaterialData mat = incomingMaterials.Single(m => m.Name == mmpc.Material.Name);
                    MaterialInfo extraInfo = new MaterialInfo();
                    extraInfo.HandlingFlags = mat.HandlingFlags;
                    extraInfo.RenderState = mat.RenderState;
                    mmpc.Tag = extraInfo;
                }
            }

            return model;
        }
        /// <summary>
        /// The main Process method converts an intermediate format content pipeline
        /// NodeContent tree to a ModelContent object with embedded animation data.
        /// </summary>
        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            ValidateMesh(input, context, null);

            // Find the skeleton.
            BoneContent skeleton = MeshHelper.FindSkeleton(input);

            if (skeleton == null)
                throw new InvalidContentException("Input skeleton not found.");

            // We don't want to have to worry about different parts of the model being
            // in different local coordinate systems, so let's just bake everything.
            FlattenTransforms(input, skeleton);

            // Read the bind pose and skeleton hierarchy data.
            IList<BoneContent> bones = MeshHelper.FlattenSkeleton(skeleton);

            // This value must match the constant of the same name in the Constants.fxh file.
            const int MAX_BONES = 72;

            if (bones.Count > MAX_BONES)
            {
                throw new InvalidContentException(string.Format(
                    "Skeleton has {0} bones, but the maximum supported is {1}.",
                    bones.Count, MAX_BONES));
            }

            List<Matrix> bindPose = new List<Matrix>();
            List<Matrix> inverseBindPose = new List<Matrix>();
            List<int> skeletonHierarchy = new List<int>();

            foreach (BoneContent bone in bones)
            {
                bindPose.Add(bone.Transform);
                inverseBindPose.Add(Matrix.Invert(bone.AbsoluteTransform));
                skeletonHierarchy.Add(bones.IndexOf(bone.Parent as BoneContent));
            }

            contentPath = Environment.CurrentDirectory;

            using (XmlReader reader = XmlReader.Create(MaterialDataFilePath))
            {
                incomingMaterials = IntermediateSerializer.Deserialize<List<MaterialData>>(reader, null);
            }
            context.AddDependency(Path.Combine(Environment.CurrentDirectory, MaterialDataFilePath));

            // Placeholder for when you could perform other ModelMeshPart/GeometryContent processing:
            //TraverseGeometryContents(input);

            AnimationPackageData incomingAnimation;

            using (XmlReader reader = XmlReader.Create(AnimationPackageDataFilePath))
            {
                incomingAnimation = IntermediateSerializer.Deserialize<AnimationPackageData>(reader, null);
            }
            context.AddDependency(Path.Combine(Environment.CurrentDirectory, AnimationPackageDataFilePath));

            // Convert animation data to our runtime format.
            Dictionary<string, Clip> animationClips;
            animationClips = ProcessAnimations(skeleton.Animations, bones, incomingAnimation.Clips);

            // Chain to the base ModelProcessor class so it can convert the model data.
            ModelContent model = base.Process(input, context);

            int modelMaxWeightsPerVert = 0;

            const string WEIGHTSPERVERT_PARAM_NAME = "gWeightsPerVert";

            // Put the material's extra data into the ModelMeshPartContent's Tag property.
            // Also, note the largest value of "WeightsPerVert" used in any material.
            foreach (ModelMeshContent mmc in model.Meshes)
            {
                foreach (ModelMeshPartContent mmpc in mmc.MeshParts)
                {
                    MaterialData mat = incomingMaterials.Single(m => m.Name == mmpc.Material.Name);
                    MaterialInfo extraInfo = new MaterialInfo();
                    extraInfo.HandlingFlags = mat.HandlingFlags;
                    extraInfo.RenderState = mat.RenderState;
                    mmpc.Tag = extraInfo;

                    EffectParam wpvEp = mat.EffectParams.Find(wpv => wpv.Name == WEIGHTSPERVERT_PARAM_NAME);
                    if (wpvEp != null)
                    {
                        modelMaxWeightsPerVert = Math.Max(modelMaxWeightsPerVert, (int)(wpvEp.Value));
                    }
                }
            }

            // Store our custom animation data in the Tag property of the model.
            SkinningData skinningData = new SkinningData(animationClips, bindPose, inverseBindPose, skeletonHierarchy, modelMaxWeightsPerVert);

            model.Tag = new AnimationPackage(
                skinningData,
                incomingAnimation.AnimationStateDescriptions,
                incomingAnimation.AnimationNodeDescriptions,
                incomingAnimation.InitialStateName,
                incomingAnimation.Transitions);

            return model;
        }