private SkinInfoContentCollection[] ProcessSkinInfo(ModelContent model)
        {
            SkinInfoContentCollection[] info     = new SkinInfoContentCollection[model.Meshes.Count];
            Dictionary <string, int>    boneDict = new Dictionary <string, int>();

            foreach (ModelBoneContent b in model.Bones)
            {
                if (b.Name != null && !boneDict.ContainsKey(b.Name))
                {
                    boneDict.Add(b.Name, b.Index);
                }
                // do cleanup on root nodes per the Boku-specific needs
                if (b.Parent == null)
                {
                    /// Null the root transform. We don't use it, and it's
                    /// a comfort to know what it is.
                    b.Transform = Matrix.Identity;
                }
            }

            for (int i = 0; i < info.Length; i++)
            {
                info[i] = new SkinInfoContentCollection();
                BoneIndexer indexer = indexers[i];
                ReadOnlyCollection <string> skinnedBoneNames = indexer.SkinnedBoneNames;

                Matrix[] absoluteTransforms = new Matrix[model.Bones.Count];
                CalculateAbsoluteTransforms(model.Bones[0], absoluteTransforms);

                Matrix absoluteMeshTransform;
                if (absoluteMeshTransforms == null)
                {
                    absoluteMeshTransform = absoluteTransforms[model.Meshes[i].ParentBone.Index];
                }
                else
                {
                    absoluteMeshTransform = absoluteMeshTransforms[i];
                }

                for (int j = 0; j < skinnedBoneNames.Count; j++)
                {
                    string          name    = skinnedBoneNames[j];
                    SkinInfoContent content = new SkinInfoContent();
                    content.BoneIndex                = boneDict[name];
                    content.PaletteIndex             = indexer.GetBoneIndex(name);
                    content.InverseBindPoseTransform = absoluteMeshTransform *
                                                       Matrix.Invert(absoluteTransforms[boneDict[name]]);
                    content.BoneName = name;
                    info[i].Add(content);
                }
            }
            return(info);
        }
        // returns true if the model contains meshes that have a parent bone as a child of
        // a bone in the skeleton attached to the mesh.
        private bool ValidateMeshSkeleton(MeshContent meshContent)
        {
            List <string> meshParentHierarchy = new List <string>();
            int           meshIndex           = (int)meshContent.OpaqueData["MeshIndex"];
            BoneIndexer   indexer             = indexers[meshIndex];

            if (indexer.SkinnedBoneNames.Contains(meshContent.Parent.Name))
            {
                // Warning
                return(false);
            }
            // skeleton is fine
            return(true);
        }
 private void CreatePaletteIndices(MeshContent mesh)
 {
     foreach (GeometryContent meshPart in mesh.Geometry)
     {
         int         meshIndex = (int)mesh.OpaqueData["MeshIndex"];
         BoneIndexer indexer   = indexers[meshIndex];
         foreach (VertexChannel channel in meshPart.Vertices.Channels)
         {
             if (channel.Name == VertexChannelNames.Weights())
             {
                 VertexChannel <BoneWeightCollection> vc =
                     (VertexChannel <BoneWeightCollection>)channel;
                 foreach (BoneWeightCollection boneWeights in vc)
                 {
                     foreach (BoneWeight weight in boneWeights)
                     {
                         indexer.GetBoneIndex(weight.BoneName);
                     }
                 }
             }
         }
     }
 }
        /// <summary>Processes a SkinnedModelImporter NodeContent root</summary>
        /// <param name="input">The root of the X file tree</param>
        /// <param name="context">The context for this processor</param>
        /// <returns>A model with animation data on its tag</returns>
        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            bool collapsed = BokuPipeline.MaterialsGroup.PreProcess(input);

            ModelSplitter splitter;

            if (context.TargetPlatform != TargetPlatform.Xbox360)
            {
                splitter = new ModelSplitter(input, 56);
            }
            else
            {
                splitter = new ModelSplitter(input, 40);
            }
            modelSplit   = splitter.Split();
            splitter     = null;
            this.input   = input;
            this.context = context;
            FindMeshes(input);
            indexers = new BoneIndexer[numMeshes];
            for (int i = 0; i < indexers.Length; i++)
            {
                indexers[i] = new BoneIndexer();
            }

            ModelContent c = base.Process(input, context);

            foreach (MeshContent meshContent in meshes)
            {
                CreatePaletteIndices(meshContent);
                BBoxer.CalcBoundingBox(meshContent, c);
            }

            // Get the process model minus the animation data

            if (!modelSplit && input.OpaqueData.ContainsKey("AbsoluteMeshTransforms"))
            {
                absoluteMeshTransforms =
                    (List <Matrix>)input.OpaqueData["AbsoluteMeshTransforms"];
            }
            else
            {
                foreach (MeshContent mesh in meshes)
                {
                    if (!ValidateMeshSkeleton(mesh))
                    {
                        context.Logger.LogWarning(null, mesh.Identity, "Warning: Mesh found that has a parent that exists as "
                                                  + "one of the bones in the skeleton attached to the mesh.  Change the mesh "
                                                  + "skeleton structure or use X - File Animation Library importer if transforms are incorrect.");
                    }
                }
            }

            Dictionary <string, object> dict = new Dictionary <string, object>();



            // Attach the animation and skinning data to the models tag
            FindAnimations(input);
            // Test to see if any animations have zero duration
            foreach (AnimationContent anim in animations.Values)
            {
                string errorMsg = "One or more AnimationContent objects have an extremely small duration.  If the animation "
                                  + "was intended to last more than one frame, please add \n AnimTicksPerSecond \n{0} \nY; \n{1}\n to your .X "
                                  + "file, where Y is a positive integer.";
                if (anim.Duration.Ticks < ContentUtil.TICKS_PER_60FPS)
                {
                    context.Logger.LogWarning("", anim.Identity, errorMsg, "{", "}");

                    break;
                }
            }

            XmlDocument xmlDoc = ReadAnimationXML(input);

            if (xmlDoc != null)
            {
                SubdivideAnimations(animations, xmlDoc);
            }

            AnimationContentDictionary processedAnims
                = new AnimationContentDictionary();

            try
            {
                foreach (KeyValuePair <string, AnimationContent> animKey in animations)
                {
                    AnimationContent processedAnim = ProcessAnimation(animKey.Value);
                    processedAnims.Add(animKey.Key, processedAnim);
                }
                dict.Add("Animations", processedAnims);
            }
            catch
            {
                throw new Exception("Error processing animations.");
            }

            BokuPipeline.MaterialsGroup.TagMeshParts(c, collapsed);

            skinInfo = ProcessSkinInfo(c);
            dict.Add("SkinInfo", skinInfo);
            c.Tag = dict;
            return(c);
        }
        /// <summary>
        /// Go through the vertex channels in the geometry and replace the
        /// BoneWeightCollection objects with weight and index channels.
        /// </summary>
        /// <param name="geometry">The geometry to process.</param>
        /// <param name="vertexChannelIndex">The index of the vertex channel to process.</param>
        /// <param name="context">The processor context.</param>
        protected override void ProcessVertexChannel(GeometryContent geometry, int vertexChannelIndex, ContentProcessorContext context)
        {
            bool boneCollectionsWithZeroWeights = false;

            if (geometry.Vertices.Channels[vertexChannelIndex].Name == VertexChannelNames.Weights())
            {
                int         meshIndex = (int)geometry.Parent.OpaqueData["MeshIndex"];
                BoneIndexer indexer   = indexers[meshIndex];
                // Skin channels are passed in from importers as BoneWeightCollection objects
                VertexChannel <BoneWeightCollection> vc =
                    (VertexChannel <BoneWeightCollection>)
                    geometry.Vertices.Channels[vertexChannelIndex];
                int maxBonesPerVertex = 0;
                for (int i = 0; i < vc.Count; i++)
                {
                    int count = vc[i].Count;
                    if (count > maxBonesPerVertex)
                    {
                        maxBonesPerVertex = count;
                    }
                }

                // Add weights as colors (Converts well to 4 floats)
                // and indices as packed 4byte vectors.
                Color[] weightsToAdd = new Color[vc.Count];
                Byte4[] indicesToAdd = new Byte4[vc.Count];

                // Go through the BoneWeightCollections and create a new
                // weightsToAdd and indicesToAdd array for each BoneWeightCollection.
                for (int i = 0; i < vc.Count; i++)
                {
                    BoneWeightCollection bwc = vc[i];

                    if (bwc.Count == 0)
                    {
                        boneCollectionsWithZeroWeights = true;
                        continue;
                    }

                    bwc.NormalizeWeights(4);
                    int count = bwc.Count;
                    if (count > maxBonesPerVertex)
                    {
                        maxBonesPerVertex = count;
                    }

                    // Add the appropriate bone indices based on the bone names in the
                    // BoneWeightCollection
                    Vector4 bi = new Vector4();
                    bi.X = count > 0 ? indexer.GetBoneIndex(bwc[0].BoneName) : (byte)0;
                    bi.Y = count > 1 ? indexer.GetBoneIndex(bwc[1].BoneName) : (byte)0;
                    bi.Z = count > 2 ? indexer.GetBoneIndex(bwc[2].BoneName) : (byte)0;
                    bi.W = count > 3 ? indexer.GetBoneIndex(bwc[3].BoneName) : (byte)0;


                    indicesToAdd[i] = new Byte4(bi);
                    Vector4 bw = new Vector4();
                    bw.X            = count > 0 ? bwc[0].Weight : 0;
                    bw.Y            = count > 1 ? bwc[1].Weight : 0;
                    bw.Z            = count > 2 ? bwc[2].Weight : 0;
                    bw.W            = count > 3 ? bwc[3].Weight : 0;
                    weightsToAdd[i] = new Color(bw);
                }

                // Remove the old BoneWeightCollection channel
                geometry.Vertices.Channels.Remove(vc);
                // Add the new channels
                geometry.Vertices.Channels.Add <Byte4>(VertexElementUsage.BlendIndices.ToString(), indicesToAdd);
                geometry.Vertices.Channels.Add <Color>(VertexElementUsage.BlendWeight.ToString(), weightsToAdd);
            }
            else
            {
                // No skinning info, so we let the base class process the channel
                base.ProcessVertexChannel(geometry, vertexChannelIndex, context);
            }
            if (boneCollectionsWithZeroWeights)
            {
                context.Logger.LogWarning("", geometry.Identity,
                                          "BonesWeightCollections with zero weights found in geometry.");
            }
        }