/// <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; }