ShatterProcessor.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // ShatterProcessor.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using System.Collections;
  12. using System.Collections.Generic;
  13. using Microsoft.Xna.Framework;
  14. using Microsoft.Xna.Framework.Graphics;
  15. using Microsoft.Xna.Framework.Content;
  16. using Microsoft.Xna.Framework.Content.Pipeline;
  17. using Microsoft.Xna.Framework.Content.Pipeline.Processors;
  18. using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
  19. #endregion
  20. namespace ShatterEffectProcessor
  21. {
  22. [ContentProcessor]
  23. public class ShatterProcessor : ModelProcessor
  24. {
  25. private string triangleCenterChannel = VertexChannelNames.TextureCoordinate(1);
  26. private string rotationalVelocityChannel =
  27. VertexChannelNames.TextureCoordinate(2);
  28. public override ModelContent Process(NodeContent input,
  29. ContentProcessorContext context)
  30. {
  31. // Break up the mesh to separate triangles.
  32. NodeContent processedNode = ProcessMesh(input);
  33. return base.Process(processedNode, context);
  34. }
  35. /// <summary>
  36. /// Breaks the input mesh into separate un-indexed triangles.
  37. /// </summary>
  38. /// <param name="input">Input MeshContent node.</param>
  39. /// <returns>Broken MeshContent</returns>
  40. private MeshContent ProcessMesh(NodeContent input)
  41. {
  42. MeshBuilder builder = MeshBuilder.StartMesh("model");
  43. MeshContent mesh = input as MeshContent;
  44. List<Vector3> normalList = new List<Vector3>();
  45. List<Vector2> texCoordList = new List<Vector2>();
  46. if (mesh != null)
  47. {
  48. int normalChannel = builder.CreateVertexChannel<Vector3>(
  49. VertexChannelNames.Normal());
  50. int texChannel = builder.CreateVertexChannel<Vector2>(
  51. VertexChannelNames.TextureCoordinate(0));
  52. foreach (GeometryContent geometry in mesh.Geometry)
  53. {
  54. IndirectPositionCollection positions = geometry.Vertices.Positions;
  55. VertexChannel<Vector3> normals =
  56. geometry.Vertices.Channels.Get<Vector3>(
  57. VertexChannelNames.Normal());
  58. VertexChannel<Vector2> texCoords =
  59. geometry.Vertices.Channels.Get<Vector2>(
  60. VertexChannelNames.TextureCoordinate(0));
  61. // Copy the positions over
  62. // To do that, we traverse the indices and grab the indexed
  63. // position and add it to the new mesh. This in effect will
  64. // duplicate positions in the mesh reversing the compacting
  65. // effect of using index buffers.
  66. foreach (int i in geometry.Indices)
  67. {
  68. builder.CreatePosition(positions[i]);
  69. // Save the normals and the texture coordinates for additon to
  70. // the mesh later.
  71. normalList.Add(normals[i]);
  72. texCoordList.Add(texCoords[i]);
  73. }
  74. }
  75. int index = 0;
  76. foreach (GeometryContent geometry in mesh.Geometry)
  77. {
  78. // Save the material to the new mesh.
  79. builder.SetMaterial(geometry.Material);
  80. // Now we create the Triangles.
  81. // To do that, we simply generate an index list that is sequential
  82. // from 0 to geometry.Indices.Count
  83. // This will create an index buffer that looks like: 0,1,2,3,4,5,...
  84. for (int i = 0; i < geometry.Indices.Count; i++)
  85. {
  86. // Set the normal for the current vertex
  87. builder.SetVertexChannelData(normalChannel, normalList[index]);
  88. // Set the texture coordinates for the current vertex
  89. builder.SetVertexChannelData(texChannel, texCoordList[index]);
  90. builder.AddTriangleVertex(index);
  91. index++;
  92. }
  93. }
  94. }
  95. MeshContent finalMesh = builder.FinishMesh();
  96. // Copy the transform over from the source mesh to retain parent/child
  97. // relative transforms.
  98. finalMesh.Transform = input.Transform;
  99. // Now we take the new MeshContent and calculate the centers of all the
  100. // triangles. The centers are needed so that we can rotate the triangles
  101. // around them as we shatter the model.
  102. foreach (GeometryContent geometry in finalMesh.Geometry)
  103. {
  104. Vector3[] triangleCenters = new Vector3[geometry.Indices.Count / 3];
  105. Vector3[] trianglePoints = new Vector3[2];
  106. IndirectPositionCollection positions = geometry.Vertices.Positions;
  107. for (int i = 0; i < positions.Count; i++)
  108. {
  109. Vector3 position = positions[i];
  110. if (i % 3 == 2)
  111. {
  112. // Calculate the center of the triangle.
  113. triangleCenters[i / 3] = (trianglePoints[0] + trianglePoints[1]
  114. + position) / 3;
  115. }
  116. else
  117. {
  118. trianglePoints[i % 3] = position;
  119. }
  120. }
  121. // Add two new channels to the MeshContent:
  122. // triangleCenterChannel: This is the channel that will store the center
  123. // of the triangle that this vertex belongs to.
  124. // rotationalVelocityChannel: This channel has randomly generated values
  125. // for x,y and z rotational angles. This information will be used to
  126. // randomly rotate the triangles as they shatter from the model.
  127. geometry.Vertices.Channels.Add<Vector3>(
  128. triangleCenterChannel,
  129. new ReplicateTriangleDataToEachVertex <Vector3>(triangleCenters));
  130. geometry.Vertices.Channels.Add<Vector3>(
  131. rotationalVelocityChannel,
  132. new ReplicateTriangleDataToEachVertex <Vector3>(
  133. new RandomVectorEnumerable(triangleCenters.Length)));
  134. }
  135. foreach (NodeContent child in input.Children)
  136. {
  137. finalMesh.Children.Add(ProcessMesh(child));
  138. }
  139. return finalMesh;
  140. }
  141. /// <summary>
  142. ///Overriding the ConvertMaterial function of ModelProcessor so that we can
  143. ///replace the BasicEffect with our own ShatterEffect.
  144. /// </summary>
  145. /// <param name="material">input Material</param>
  146. /// <param name="context">Content processor context</param>
  147. /// <returns></returns>
  148. protected override MaterialContent ConvertMaterial(MaterialContent material,
  149. ContentProcessorContext context)
  150. {
  151. EffectMaterialContent effect = new EffectMaterialContent();
  152. // Use our own ShatterEffect.fx instead of BasicEffect.
  153. effect.Effect =
  154. new ExternalReference<EffectContent>("shatterEffect.fx");
  155. foreach (ExternalReference<TextureContent> texture in
  156. material.Textures.Values)
  157. {
  158. // Add the textures in the source Material to our effect.
  159. effect.Textures.Add("modelTexture", texture);
  160. }
  161. return base.ConvertMaterial(effect, context);
  162. }
  163. }
  164. }