//----------------------------------------------------------------------------- // ShatterProcessor.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Content.Pipeline; using Microsoft.Xna.Framework.Content.Pipeline.Processors; using Microsoft.Xna.Framework.Content.Pipeline.Graphics; namespace ShatterEffectProcessor { [ContentProcessor] public class ShatterProcessor : ModelProcessor { private string triangleCenterChannel = VertexChannelNames.TextureCoordinate(1); private string rotationalVelocityChannel = VertexChannelNames.TextureCoordinate(2); public override ModelContent Process(NodeContent input, ContentProcessorContext context) { // Break up the mesh to separate triangles. NodeContent processedNode = ProcessMesh(input); return base.Process(processedNode, context); } /// /// Breaks the input mesh into separate un-indexed triangles. /// /// Input MeshContent node. /// Broken MeshContent private MeshContent ProcessMesh(NodeContent input) { MeshBuilder builder = MeshBuilder.StartMesh("model"); MeshContent mesh = input as MeshContent; List normalList = new List(); List texCoordList = new List(); if (mesh != null) { int normalChannel = builder.CreateVertexChannel( VertexChannelNames.Normal()); int texChannel = builder.CreateVertexChannel( VertexChannelNames.TextureCoordinate(0)); foreach (GeometryContent geometry in mesh.Geometry) { IndirectPositionCollection positions = geometry.Vertices.Positions; VertexChannel normals = geometry.Vertices.Channels.Get( VertexChannelNames.Normal()); VertexChannel texCoords = geometry.Vertices.Channels.Get( VertexChannelNames.TextureCoordinate(0)); // Copy the positions over // To do that, we traverse the indices and grab the indexed // position and add it to the new mesh. This in effect will // duplicate positions in the mesh reversing the compacting // effect of using index buffers. foreach (int i in geometry.Indices) { builder.CreatePosition(positions[i]); // Save the normals and the texture coordinates for additon to // the mesh later. normalList.Add(normals[i]); texCoordList.Add(texCoords[i]); } } int index = 0; foreach (GeometryContent geometry in mesh.Geometry) { // Save the material to the new mesh. builder.SetMaterial(geometry.Material); // Now we create the Triangles. // To do that, we simply generate an index list that is sequential // from 0 to geometry.Indices.Count // This will create an index buffer that looks like: 0,1,2,3,4,5,... for (int i = 0; i < geometry.Indices.Count; i++) { // Set the normal for the current vertex builder.SetVertexChannelData(normalChannel, normalList[index]); // Set the texture coordinates for the current vertex builder.SetVertexChannelData(texChannel, texCoordList[index]); builder.AddTriangleVertex(index); index++; } } } MeshContent finalMesh = builder.FinishMesh(); // Copy the transform over from the source mesh to retain parent/child // relative transforms. finalMesh.Transform = input.Transform; // Now we take the new MeshContent and calculate the centers of all the // triangles. The centers are needed so that we can rotate the triangles // around them as we shatter the model. foreach (GeometryContent geometry in finalMesh.Geometry) { Vector3[] triangleCenters = new Vector3[geometry.Indices.Count / 3]; Vector3[] trianglePoints = new Vector3[2]; IndirectPositionCollection positions = geometry.Vertices.Positions; for (int i = 0; i < positions.Count; i++) { Vector3 position = positions[i]; if (i % 3 == 2) { // Calculate the center of the triangle. triangleCenters[i / 3] = (trianglePoints[0] + trianglePoints[1] + position) / 3; } else { trianglePoints[i % 3] = position; } } // Add two new channels to the MeshContent: // triangleCenterChannel: This is the channel that will store the center // of the triangle that this vertex belongs to. // rotationalVelocityChannel: This channel has randomly generated values // for x,y and z rotational angles. This information will be used to // randomly rotate the triangles as they shatter from the model. geometry.Vertices.Channels.Add( triangleCenterChannel, new ReplicateTriangleDataToEachVertex(triangleCenters)); geometry.Vertices.Channels.Add( rotationalVelocityChannel, new ReplicateTriangleDataToEachVertex( new RandomVectorEnumerable(triangleCenters.Length))); } foreach (NodeContent child in input.Children) { finalMesh.Children.Add(ProcessMesh(child)); } return finalMesh; } /// ///Overriding the ConvertMaterial function of ModelProcessor so that we can ///replace the BasicEffect with our own ShatterEffect. /// /// input Material /// Content processor context /// protected override MaterialContent ConvertMaterial(MaterialContent material, ContentProcessorContext context) { EffectMaterialContent effect = new EffectMaterialContent(); // Use our own ShatterEffect.fx instead of BasicEffect. effect.Effect = new ExternalReference("shatterEffect.fx"); foreach (ExternalReference texture in material.Textures.Values) { // Add the textures in the source Material to our effect. effect.Textures.Add("modelTexture", texture); } return base.ConvertMaterial(effect, context); } } }