TerrainProcessor.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // TerrainProcessor.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.ComponentModel;
  11. using System.IO;
  12. using Microsoft.Xna.Framework;
  13. using Microsoft.Xna.Framework.Content.Pipeline;
  14. using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
  15. using Microsoft.Xna.Framework.Content.Pipeline.Processors;
  16. #endregion
  17. namespace HeightmapCollisionPipeline
  18. {
  19. /// <summary>
  20. /// Custom content processor for creating terrain meshes. Given an
  21. /// input heightfield texture, this processor uses the MeshBuilder
  22. /// class to programatically generate terrain geometry.
  23. /// </summary>
  24. [ContentProcessor]
  25. public class TerrainProcessor : ContentProcessor<Texture2DContent, ModelContent>
  26. {
  27. #region Properties
  28. private float terrainScale = 30f;
  29. [DisplayName("Terrain Scale")]
  30. [DefaultValue(30f)]
  31. [Description("Scale of the the terrain geometry width and length.")]
  32. public float TerrainScale
  33. {
  34. get { return terrainScale; }
  35. set { terrainScale = value; }
  36. }
  37. private float terrainBumpiness = 640f;
  38. [DisplayName("Terrain Bumpiness")]
  39. [DefaultValue(640f)]
  40. [Description("Scale of the the terrain geometry height.")]
  41. public float TerrainBumpiness
  42. {
  43. get { return terrainBumpiness; }
  44. set { terrainBumpiness = value; }
  45. }
  46. private float texCoordScale = 0.1f;
  47. [DisplayName("Texture Coordinate Scale")]
  48. [DefaultValue(0.1f)]
  49. [Description("Terrain texture tiling density.")]
  50. public float TexCoordScale
  51. {
  52. get { return texCoordScale; }
  53. set { texCoordScale = value; }
  54. }
  55. private string terrainTextureFilename = "rocks.bmp";
  56. [DisplayName("Terrain Texture")]
  57. [DefaultValue("rocks.bmp")]
  58. [Description("The name of the terrain texture.")]
  59. public string TerrainTextureFilename
  60. {
  61. get { return terrainTextureFilename; }
  62. set { terrainTextureFilename = value; }
  63. }
  64. #endregion
  65. /// <summary>
  66. /// Generates a terrain mesh from an input heightfield texture.
  67. /// </summary>
  68. public override ModelContent Process(Texture2DContent input,
  69. ContentProcessorContext context)
  70. {
  71. MeshBuilder builder = MeshBuilder.StartMesh("terrain");
  72. // Convert the input texture to float format, for ease of processing.
  73. input.ConvertBitmapType(typeof(PixelBitmapContent<float>));
  74. PixelBitmapContent<float> heightfield;
  75. heightfield = (PixelBitmapContent<float>)input.Mipmaps[0];
  76. // Create the terrain vertices.
  77. for (int y = 0; y < heightfield.Height; y++)
  78. {
  79. for (int x = 0; x < heightfield.Width; x++)
  80. {
  81. Vector3 position;
  82. // position the vertices so that the heightfield is centered
  83. // around x=0,z=0
  84. position.X = terrainScale * (x - ((heightfield.Width - 1) / 2.0f));
  85. position.Z = terrainScale * (y - ((heightfield.Height - 1) / 2.0f));
  86. position.Y = (heightfield.GetPixel(x, y) - 1) * terrainBumpiness;
  87. builder.CreatePosition(position);
  88. }
  89. }
  90. // Create a material, and point it at our terrain texture.
  91. BasicMaterialContent material = new BasicMaterialContent();
  92. material.SpecularColor = new Vector3(.4f, .4f, .4f);
  93. string directory = Path.GetDirectoryName(input.Identity.SourceFilename);
  94. string texture = Path.Combine(directory, terrainTextureFilename);
  95. material.Texture = new ExternalReference<TextureContent>(texture);
  96. builder.SetMaterial(material);
  97. // Create a vertex channel for holding texture coordinates.
  98. int texCoordId = builder.CreateVertexChannel<Vector2>(
  99. VertexChannelNames.TextureCoordinate(0));
  100. // Create the individual triangles that make up our terrain.
  101. for (int y = 0; y < heightfield.Height - 1; y++)
  102. {
  103. for (int x = 0; x < heightfield.Width - 1; x++)
  104. {
  105. AddVertex(builder, texCoordId, heightfield.Width, x, y);
  106. AddVertex(builder, texCoordId, heightfield.Width, x + 1, y);
  107. AddVertex(builder, texCoordId, heightfield.Width, x + 1, y + 1);
  108. AddVertex(builder, texCoordId, heightfield.Width, x, y);
  109. AddVertex(builder, texCoordId, heightfield.Width, x + 1, y + 1);
  110. AddVertex(builder, texCoordId, heightfield.Width, x, y + 1);
  111. }
  112. }
  113. // Chain to the ModelProcessor so it can convert the mesh we just generated.
  114. MeshContent terrainMesh = builder.FinishMesh();
  115. ModelContent model = context.Convert<MeshContent, ModelContent>(terrainMesh,
  116. "ModelProcessor");
  117. // generate information about the height map, and attach it to the finished
  118. // model's tag.
  119. model.Tag = new HeightMapInfoContent(heightfield, terrainScale,
  120. terrainBumpiness);
  121. return model;
  122. }
  123. /// <summary>
  124. /// Helper for adding a new triangle vertex to a MeshBuilder,
  125. /// along with an associated texture coordinate value.
  126. /// </summary>
  127. void AddVertex(MeshBuilder builder, int texCoordId, int w, int x, int y)
  128. {
  129. builder.SetVertexChannelData(texCoordId, new Vector2(x, y) * texCoordScale);
  130. builder.AddTriangleVertex(x + y * w);
  131. }
  132. }
  133. }