/Source/HelixToolkit.Wpf/Visual3Ds/Terrain/TerrainModel.cs

http://helixtoolkit.codeplex.com · C# · 279 lines · 149 code · 35 blank · 95 comment · 17 complexity · a0727091a7fcd1ca984052305700861f MD5 · raw file

  1. // --------------------------------------------------------------------------------------------------------------------
  2. // <copyright file="TerrainModel.cs" company="Helix 3D Toolkit">
  3. // http://helixtoolkit.codeplex.com, license: MIT
  4. // </copyright>
  5. // --------------------------------------------------------------------------------------------------------------------
  6. namespace HelixToolkit.Wpf
  7. {
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.IO.Compression;
  12. using System.Text;
  13. using System.Windows.Media.Media3D;
  14. /// <summary>
  15. /// Represents a terrain model.
  16. /// </summary>
  17. /// <remarks>
  18. /// Supports the following terrain file types
  19. /// .bt
  20. /// .btz
  21. /// <para>
  22. /// Read .bt files from disk, keeps the model data and creates the Model3D.
  23. /// The .btz format is a gzip compressed version of the .bt format.
  24. /// </para>
  25. /// </remarks>
  26. public class TerrainModel
  27. {
  28. /// <summary>
  29. /// Gets or sets the bottom.
  30. /// </summary>
  31. /// <value>The bottom.</value>
  32. public double Bottom { get; set; }
  33. /// <summary>
  34. /// Gets or sets the data.
  35. /// </summary>
  36. /// <value>The data.</value>
  37. public double[] Data { get; set; }
  38. /// <summary>
  39. /// Gets or sets the height.
  40. /// </summary>
  41. /// <value>The height.</value>
  42. public int Height { get; set; }
  43. /// <summary>
  44. /// Gets or sets the left.
  45. /// </summary>
  46. /// <value>The left.</value>
  47. public double Left { get; set; }
  48. /// <summary>
  49. /// Gets or sets the maximum Z.
  50. /// </summary>
  51. /// <value>The maximum Z.</value>
  52. public double MaximumZ { get; set; }
  53. /// <summary>
  54. /// Gets or sets the minimum Z.
  55. /// </summary>
  56. /// <value>The minimum Z.</value>
  57. public double MinimumZ { get; set; }
  58. /// <summary>
  59. /// Gets or sets the offset.
  60. /// </summary>
  61. /// <value>The offset.</value>
  62. public Point3D Offset { get; set; }
  63. /// <summary>
  64. /// Gets or sets the right.
  65. /// </summary>
  66. /// <value>The right.</value>
  67. public double Right { get; set; }
  68. /// <summary>
  69. /// Gets or sets the texture.
  70. /// </summary>
  71. /// <value>The texture.</value>
  72. public TerrainTexture Texture { get; set; }
  73. /// <summary>
  74. /// Gets or sets the top.
  75. /// </summary>
  76. /// <value>The top.</value>
  77. public double Top { get; set; }
  78. /// <summary>
  79. /// Gets or sets the width.
  80. /// </summary>
  81. /// <value>The width.</value>
  82. public int Width { get; set; }
  83. /// <summary>
  84. /// Creates the 3D model of the terrain.
  85. /// </summary>
  86. /// <param name="lod">
  87. /// The level of detail.
  88. /// </param>
  89. /// <returns>
  90. /// The Model3D.
  91. /// </returns>
  92. public GeometryModel3D CreateModel(int lod)
  93. {
  94. int ni = this.Height / lod;
  95. int nj = this.Width / lod;
  96. var pts = new List<Point3D>(ni * nj);
  97. double mx = (this.Left + this.Right) / 2;
  98. double my = (this.Top + this.Bottom) / 2;
  99. double mz = (this.MinimumZ + this.MaximumZ) / 2;
  100. this.Offset = new Point3D(mx, my, mz);
  101. for (int i = 0; i < ni; i++)
  102. {
  103. for (int j = 0; j < nj; j++)
  104. {
  105. double x = this.Left + (this.Right - this.Left) * j / (nj - 1);
  106. double y = this.Top + (this.Bottom - this.Top) * i / (ni - 1);
  107. double z = this.Data[i * lod * this.Width + j * lod];
  108. x -= this.Offset.X;
  109. y -= this.Offset.Y;
  110. z -= this.Offset.Z;
  111. pts.Add(new Point3D(x, y, z));
  112. }
  113. }
  114. var mb = new MeshBuilder(false, false);
  115. mb.AddRectangularMesh(pts, nj);
  116. var mesh = mb.ToMesh();
  117. var material = Materials.Green;
  118. if (this.Texture != null)
  119. {
  120. this.Texture.Calculate(this, mesh);
  121. material = this.Texture.Material;
  122. mesh.TextureCoordinates = this.Texture.TextureCoordinates;
  123. }
  124. return new GeometryModel3D { Geometry = mesh, Material = material, BackMaterial = material };
  125. }
  126. /// <summary>
  127. /// Loads the specified file.
  128. /// </summary>
  129. /// <param name="source">
  130. /// The file name.
  131. /// </param>
  132. public void Load(string source)
  133. {
  134. if (source == null)
  135. {
  136. throw new ArgumentNullException("source");
  137. }
  138. var ext = Path.GetExtension(source);
  139. if (ext != null)
  140. {
  141. ext = ext.ToLower();
  142. }
  143. switch (ext)
  144. {
  145. case ".btz":
  146. this.ReadZippedFile(source);
  147. break;
  148. case ".bt":
  149. this.ReadTerrainFile(source);
  150. break;
  151. }
  152. }
  153. /// <summary>
  154. /// Reads a .bt (Binary terrain) file.
  155. /// http://www.vterrain.org/Implementation/Formats/BT.html
  156. /// </summary>
  157. /// <param name="stream">
  158. /// The stream.
  159. /// </param>
  160. public void ReadTerrainFile(Stream stream)
  161. {
  162. using (var reader = new BinaryReader(stream))
  163. {
  164. var buffer = reader.ReadBytes(10);
  165. var enc = new ASCIIEncoding();
  166. var marker = enc.GetString(buffer);
  167. if (!marker.StartsWith("binterr"))
  168. {
  169. throw new FileFormatException("Invalid marker.");
  170. }
  171. var version = marker.Substring(7);
  172. this.Width = reader.ReadInt32();
  173. this.Height = reader.ReadInt32();
  174. short dataSize = reader.ReadInt16();
  175. bool isFloatingPoint = reader.ReadInt16() == 1;
  176. short horizontalUnits = reader.ReadInt16();
  177. short utmZone = reader.ReadInt16();
  178. short datum = reader.ReadInt16();
  179. this.Left = reader.ReadDouble();
  180. this.Right = reader.ReadDouble();
  181. this.Bottom = reader.ReadDouble();
  182. this.Top = reader.ReadDouble();
  183. short proj = reader.ReadInt16();
  184. float scale = reader.ReadSingle();
  185. var padding = reader.ReadBytes(190);
  186. int index = 0;
  187. this.Data = new double[this.Width * this.Height];
  188. this.MinimumZ = double.MaxValue;
  189. this.MaximumZ = double.MinValue;
  190. for (int y = 0; y < this.Height; y++)
  191. {
  192. for (int x = 0; x < this.Width; x++)
  193. {
  194. double z;
  195. if (dataSize == 2)
  196. {
  197. z = reader.ReadUInt16();
  198. }
  199. else
  200. {
  201. z = isFloatingPoint ? reader.ReadSingle() : reader.ReadUInt32();
  202. }
  203. this.Data[index++] = z;
  204. if (z < this.MinimumZ)
  205. {
  206. this.MinimumZ = z;
  207. }
  208. if (z > this.MaximumZ)
  209. {
  210. this.MaximumZ = z;
  211. }
  212. }
  213. }
  214. }
  215. }
  216. /// <summary>
  217. /// Reads the specified .bt terrain file.
  218. /// </summary>
  219. /// <param name="path">
  220. /// The file name.
  221. /// </param>
  222. private void ReadTerrainFile(string path)
  223. {
  224. using (var infile = File.OpenRead(path))
  225. {
  226. this.ReadTerrainFile(infile);
  227. }
  228. }
  229. /// <summary>
  230. /// Read a gzipped .bt file.
  231. /// </summary>
  232. /// <param name="source">
  233. /// The source.
  234. /// </param>
  235. private void ReadZippedFile(string source)
  236. {
  237. using (var infile = File.OpenRead(source))
  238. {
  239. var deflateStream = new GZipStream(infile, CompressionMode.Decompress, true);
  240. this.ReadTerrainFile(deflateStream);
  241. }
  242. }
  243. }
  244. }