private ModelContent SkinnedData(ModelContent model, NodeContent input, ContentProcessorContext context, MeshMetaData metadata)
        {
            // Find the skeleton
            BoneContent skeleton = MeshHelper.FindSkeleton(input);

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

                // Set up the bone matrices and index lists
                List<Matrix> bindPose = new List<Matrix>();
                List<Matrix> inverseBindPose = new List<Matrix>();
                List<int> skeletonHierarchy = new List<int>();
                Dictionary<String, int> boneIndices = new Dictionary<String, int>();
                Dictionary<String, AnimationClip> animationClips = new Dictionary<String, AnimationClip>();

                // Extract the bind pose transforms, inverse bind pose transforms,
                // and parent bone index of each bone in order
                foreach (BoneContent bone in bones)
                {
                    bindPose.Add(bone.Transform);
                    inverseBindPose.Add(Matrix.Invert(bone.AbsoluteTransform));
                    skeletonHierarchy.Add(bones.IndexOf(bone.Parent as BoneContent));
                    boneIndices.Add(bone.Name, boneIndices.Count);
                }

                // Convert animation data to our runtime format.
                animationClips = ProcessAnimations(skeleton.Animations, bones);

                // Save skinndd data
                metadata.SkinningData = new SkinningData(animationClips, bindPose, inverseBindPose, skeletonHierarchy, boneIndices);
            }

            // Save meta data
            model.Tag = metadata;

            // Return the model with the custom animation data
            return model;
        }
        private BoundingBox ComputeBoundingBox(NodeContent input, ref BoundingBox aabb, MeshMetaData metadata)
        {
            BoundingBox boundingBox;

            if (input is MeshContent)
            {
                MeshContent mc = (MeshContent)input;
                MeshHelper.TransformScene(mc, mc.Transform);
                mc.Transform = Matrix.Identity;

                // Create bounding box
                boundingBox = BoundingBox.CreateFromPoints(mc.Positions);

                //create sub mesh information
                MeshMetaData.SubMeshMetadata subMeshMetadata = new MeshMetaData.SubMeshMetadata();
                subMeshMetadata.BoundingBox = boundingBox;
                subMeshMetadata.RenderQueue = m_RenderQueue;
                subMeshMetadata.ShadowCullMode = m_ShadowCullMode;
                subMeshMetadata.EnableLighting = m_EnableLighting;
                subMeshMetadata.UseNormalMap = m_EnableNormalMap;
                subMeshMetadata.CastShadows = m_CastShadows;

                // Add sub mesh meta data
                metadata.AddSubMeshMetadata(subMeshMetadata);

                // merge bounding boxes of sub parts
                if (metadata.SubMeshesMetadata.Count > 1)
                    boundingBox = BoundingBox.CreateMerged(boundingBox, aabb);
            }
            else
            {
                boundingBox = aabb;
            }

            // Run through children of node and merge children
            foreach (NodeContent c in input.Children)
            {
                boundingBox = BoundingBox.CreateMerged(boundingBox, ComputeBoundingBox(c, ref boundingBox, metadata));
            }

            return boundingBox;
        }
        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            if (input == null)
            {
                throw new ArgumentNullException("input");
            }

            // Get directory
            m_Directory = Path.GetDirectoryName(input.Identity.SourceFilename);

            // Look up textures
            LookUpTextures(input);

            // merge transforms
            MeshHelper.TransformScene(input, input.Transform);
            input.Transform = Matrix.Identity;

            // Before anything rotate the entire model and animations
            // I use separate animation files so I have to remember to
            // rotated the animations the same way as the 
            // recipient of those animations.
            RotateAll(input, DegreesX, DegreesY, DegreesZ);

            if (!string.IsNullOrEmpty(MergeAnimations))
            {
                foreach (string mergeFile in MergeAnimations.Split(';').Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)))
                {
                    MergeAnimation(input, context, mergeFile);
                }
            }

            if (!m_IsSkinned)
            {
                // Not a skinned model so merge transforms for optimisations
                MergeTransforms(input);
            }

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

            // Create model mesh data tag
            MeshMetaData metadata = new MeshMetaData();

            // Normal skinning data
            model = SkinnedData(model, input, context, metadata);

            //gather some information that will be useful in run time
            BoundingBox aabb = new BoundingBox();
            metadata.BoundingBox = ComputeBoundingBox(input, ref aabb, metadata);

            // Return model data
            return model;
        }