ShatterProcessor.cs 8.0 KB

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