#region File Description //----------------------------------------------------------------------------- // CustomModelProcessor.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Content.Pipeline; using Microsoft.Xna.Framework.Content.Pipeline.Graphics; using Microsoft.Xna.Framework.Content.Pipeline.Processors; #endregion namespace CustomModelPipeline { /// /// Content Pipeline processor converts incoming /// graphics data into our custom model format. /// [ContentProcessor(DisplayName = "Custom Model")] public class CustomModelProcessor : ContentProcessor { #region Fields ContentProcessorContext context; CustomModelContent outputModel; // A single material may be reused on more than one piece of geometry. // This dictionary keeps track of materials we have already converted, // to make sure we only bother processing each of them once. Dictionary processedMaterials = new Dictionary(); #endregion /// /// Converts incoming graphics data into our custom model format. /// public override CustomModelContent Process(NodeContent input, ContentProcessorContext context) { this.context = context; outputModel = new CustomModelContent(); ProcessNode(input); return outputModel; } /// /// Recursively processes a node from the input data tree. /// void ProcessNode(NodeContent node) { // Meshes can contain internal hierarchy (nested tranforms, joints, bones, // etc), but this sample isn't going to bother storing any of that data. // Instead we will just bake any node transforms into the geometry, after // which we can reset them to identity and forget all about them. MeshHelper.TransformScene(node, node.Transform); node.Transform = Matrix.Identity; // Is this node in fact a mesh? MeshContent mesh = node as MeshContent; if (mesh != null) { // Reorder vertex and index data so triangles will render in // an order that makes efficient use of the GPU vertex cache. MeshHelper.OptimizeForCache(mesh); // Process all the geometry in the mesh. foreach (GeometryContent geometry in mesh.Geometry) { ProcessGeometry(geometry); } } // Recurse over any child nodes. foreach (NodeContent child in node.Children) { ProcessNode(child); } } /// /// Converts a single piece of input geometry into our custom format. /// void ProcessGeometry(GeometryContent geometry) { int triangleCount = geometry.Indices.Count / 3; int vertexCount = geometry.Vertices.VertexCount; // Flatten the flexible input vertex channel data into // a simple GPU style vertex buffer byte array. VertexBufferContent vertexBufferContent = geometry.Vertices.CreateVertexBuffer(); // Convert the input material. MaterialContent material = ProcessMaterial(geometry.Material); // Add the new piece of geometry to our output model. outputModel.AddModelPart(triangleCount, vertexCount, vertexBufferContent, geometry.Indices, material); } /// /// Converts an input material by chaining to the built-in MaterialProcessor /// class. This will automatically go off and build any effects or textures /// that are referenced by the material. When you load the resulting material /// at runtime, you will get back an Effect instance that has the appropriate /// textures already loaded into it and ready to go. /// MaterialContent ProcessMaterial(MaterialContent material) { // Have we already processed this material? if (!processedMaterials.ContainsKey(material)) { // If not, process it now. processedMaterials[material] = context.Convert(material, "MaterialProcessor"); } return processedMaterials[material]; } } }