TerrainEngine.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using libTerrain;
  5. namespace OpenSim.Terrain
  6. {
  7. public class TerrainEngine
  8. {
  9. /// <summary>
  10. /// A [normally] 256x256 heightmap
  11. /// </summary>
  12. public Channel heightmap;
  13. /// <summary>
  14. /// Whether or not the terrain has been modified since it was last saved and sent to the Physics engine.
  15. /// Counts the number of modifications since the last save. (0 = Untainted)
  16. /// </summary>
  17. public int tainted;
  18. int w, h;
  19. /// <summary>
  20. /// Generate a new TerrainEngine instance and creates a new heightmap
  21. /// </summary>
  22. public TerrainEngine()
  23. {
  24. w = 256;
  25. h = 256;
  26. heightmap = new Channel(w, h);
  27. tainted++;
  28. }
  29. /// <summary>
  30. /// Converts the heightmap to a 65536 value 1D floating point array
  31. /// </summary>
  32. /// <returns>A float[65536] array containing the heightmap</returns>
  33. public float[] getHeights1D()
  34. {
  35. float[] heights = new float[w * h];
  36. int i;
  37. for (i = 0; i < w * h; i++)
  38. {
  39. heights[i] = (float)heightmap.map[i / w, i % w];
  40. }
  41. return heights;
  42. }
  43. /// <summary>
  44. /// Converts the heightmap to a 256x256 value 2D floating point array.
  45. /// </summary>
  46. /// <returns>An array of 256,256 values containing the heightmap</returns>
  47. public float[,] getHeights2D()
  48. {
  49. float[,] heights = new float[w, h];
  50. int x, y;
  51. for (x = 0; x < w; x++)
  52. {
  53. for (y = 0; y < h; y++)
  54. {
  55. heights[x, y] = (float)heightmap.map[x, y];
  56. }
  57. }
  58. return heights;
  59. }
  60. /// <summary>
  61. /// Imports a 1D floating point array into the 2D heightmap array
  62. /// </summary>
  63. /// <param name="heights">The array to import (must have 65536 members)</param>
  64. public void setHeights1D(float[] heights)
  65. {
  66. int i;
  67. for (i = 0; i < w * h; i++)
  68. {
  69. heightmap.map[i / w, i % w] = heights[i];
  70. }
  71. tainted++;
  72. }
  73. /// <summary>
  74. /// Loads a 2D array of values into the heightmap
  75. /// </summary>
  76. /// <param name="heights">An array of 256,256 float values</param>
  77. public void setHeights2D(float[,] heights)
  78. {
  79. int x, y;
  80. for (x = 0; x < w; x++)
  81. {
  82. for (y = 0; y < h; y++)
  83. {
  84. heightmap.set(x,y,(double)heights[x,y]);
  85. }
  86. }
  87. tainted++;
  88. }
  89. /// <summary>
  90. /// Processes a terrain-specific command
  91. /// </summary>
  92. /// <param name="args">Commandline arguments (space seperated)</param>
  93. /// <param name="resultText">Reference that returns error or help text if returning false</param>
  94. /// <returns>If the operation was successful (if not, the error is placed into resultText)</returns>
  95. public bool RunTerrainCmd(string[] args, ref string resultText)
  96. {
  97. string command = args[0];
  98. try
  99. {
  100. switch (command)
  101. {
  102. case "help":
  103. resultText += "terrain regenerate - rebuilds the sims terrain using a default algorithm\n";
  104. resultText += "terrain seed <seed> - sets the random seed value to <seed>\n";
  105. resultText += "terrain load <type> <filename> - loads a terrain from disk, type can be 'F32', 'F64' or 'IMG'\n";
  106. resultText += "terrain save <type> <filename> - saves a terrain to disk, type can be 'F32' or 'F64'\n";
  107. resultText += "terrain rescale <min> <max> - rescales a terrain to be between <min> and <max> meters high\n";
  108. resultText += "terrain erode aerobic <windspeed> <pickupmin> <dropmin> <carry> <rounds> <lowest>\n";
  109. resultText += "terrain erode thermal <talus> <rounds> <carry>\n";
  110. resultText += "terrain multiply <val> - multiplies a terrain by <val>\n";
  111. return false;
  112. case "seed":
  113. setSeed(Convert.ToInt32(args[1]));
  114. break;
  115. case "erode":
  116. switch (args[1].ToLower())
  117. {
  118. case "aerobic":
  119. // WindSpeed, PickupMinimum,DropMinimum,Carry,Rounds,Lowest
  120. heightmap.AerobicErosion(Convert.ToDouble(args[2]), Convert.ToDouble(args[3]), Convert.ToDouble(args[4]), Convert.ToDouble(args[5]), Convert.ToInt32(args[6]), Convert.ToBoolean(args[7]));
  121. break;
  122. case "thermal":
  123. heightmap.thermalWeathering(Convert.ToDouble(args[2]), Convert.ToInt32(args[3]), Convert.ToDouble(args[4]));
  124. break;
  125. default:
  126. resultText = "Unknown erosion type";
  127. return false;
  128. }
  129. break;
  130. case "regenerate":
  131. hills();
  132. break;
  133. case "rescale":
  134. setRange(Convert.ToSingle(args[1]), Convert.ToSingle(args[2]));
  135. break;
  136. case "multiply":
  137. heightmap *= Convert.ToDouble(args[1]);
  138. break;
  139. case "load":
  140. switch (args[1].ToLower())
  141. {
  142. case "f32":
  143. loadFromFileF32(args[2]);
  144. break;
  145. case "f64":
  146. loadFromFileF64(args[2]);
  147. break;
  148. case "img":
  149. resultText = "Error - IMG mode is presently unsupported.";
  150. return false;
  151. default:
  152. resultText = "Unknown image or data format";
  153. return false;
  154. }
  155. break;
  156. case "save":
  157. switch (args[1].ToLower())
  158. {
  159. case "f32":
  160. writeToFileF32(args[2]);
  161. break;
  162. case "f64":
  163. writeToFileF64(args[2]);
  164. break;
  165. default:
  166. resultText = "Unknown image or data format";
  167. return false;
  168. }
  169. break;
  170. default:
  171. resultText = "Unknown terrain command";
  172. return false;
  173. }
  174. return true;
  175. }
  176. catch (Exception e)
  177. {
  178. resultText = "Error running terrain command: " + e.ToString();
  179. return false;
  180. }
  181. }
  182. /// <summary>
  183. /// Renormalises the array between min and max
  184. /// </summary>
  185. /// <param name="min">Minimum value of the new array</param>
  186. /// <param name="max">Maximum value of the new array</param>
  187. public void setRange(float min, float max)
  188. {
  189. heightmap.normalise((double)min, (double)max);
  190. tainted++;
  191. }
  192. /// <summary>
  193. /// Loads a file consisting of 256x256 doubles and imports it as an array into the map.
  194. /// </summary>
  195. /// <remarks>TODO: Move this to libTerrain itself</remarks>
  196. /// <param name="filename">The filename of the double array to import</param>
  197. public void loadFromFileF64(string filename)
  198. {
  199. System.IO.FileInfo file = new System.IO.FileInfo(filename);
  200. System.IO.FileStream s = file.Open(System.IO.FileMode.Open, System.IO.FileAccess.Read);
  201. System.IO.BinaryReader bs = new System.IO.BinaryReader(s);
  202. int x, y;
  203. for (x = 0; x < w; x++)
  204. {
  205. for (y = 0; y < h; y++)
  206. {
  207. heightmap.map[x, y] = bs.ReadDouble();
  208. }
  209. }
  210. bs.Close();
  211. s.Close();
  212. tainted++;
  213. }
  214. /// <summary>
  215. /// Loads a file consisting of 256x256 floats and imports it as an array into the map.
  216. /// </summary>
  217. /// <remarks>TODO: Move this to libTerrain itself</remarks>
  218. /// <param name="filename">The filename of the float array to import</param>
  219. public void loadFromFileF32(string filename)
  220. {
  221. System.IO.FileInfo file = new System.IO.FileInfo(filename);
  222. System.IO.FileStream s = file.Open(System.IO.FileMode.Open, System.IO.FileAccess.Read);
  223. System.IO.BinaryReader bs = new System.IO.BinaryReader(s);
  224. int x, y;
  225. for (x = 0; x < w; x++)
  226. {
  227. for (y = 0; y < h; y++)
  228. {
  229. heightmap.map[x, y] = (double)bs.ReadSingle();
  230. }
  231. }
  232. bs.Close();
  233. s.Close();
  234. tainted++;
  235. }
  236. /// <summary>
  237. /// Writes the current terrain heightmap to disk, in the format of a 65536 entry double[] array.
  238. /// </summary>
  239. /// <param name="filename">The desired output filename</param>
  240. public void writeToFileF64(string filename)
  241. {
  242. System.IO.FileInfo file = new System.IO.FileInfo(filename);
  243. System.IO.FileStream s = file.Open(System.IO.FileMode.CreateNew, System.IO.FileAccess.Write);
  244. System.IO.BinaryWriter bs = new System.IO.BinaryWriter(s);
  245. int x, y;
  246. for (x = 0; x < w; x++)
  247. {
  248. for (y = 0; y < h; y++)
  249. {
  250. bs.Write(heightmap.get(x,y));
  251. }
  252. }
  253. bs.Close();
  254. s.Close();
  255. }
  256. /// <summary>
  257. /// Writes the current terrain heightmap to disk, in the format of a 65536 entry float[] array
  258. /// </summary>
  259. /// <param name="filename">The desired output filename</param>
  260. public void writeToFileF32(string filename)
  261. {
  262. System.IO.FileInfo file = new System.IO.FileInfo(filename);
  263. System.IO.FileStream s = file.Open(System.IO.FileMode.CreateNew, System.IO.FileAccess.Write);
  264. System.IO.BinaryWriter bs = new System.IO.BinaryWriter(s);
  265. int x, y;
  266. for (x = 0; x < w; x++)
  267. {
  268. for (y = 0; y < h; y++)
  269. {
  270. bs.Write((float)heightmap.get(x, y));
  271. }
  272. }
  273. bs.Close();
  274. s.Close();
  275. }
  276. /// <summary>
  277. /// Sets the random seed to be used by procedural functions which involve random numbers.
  278. /// </summary>
  279. /// <param name="val">The desired seed</param>
  280. public void setSeed(int val)
  281. {
  282. heightmap.seed = val;
  283. }
  284. /// <summary>
  285. /// Raises land in a sphere around the specified coordinates
  286. /// </summary>
  287. /// <param name="rx">Center of the sphere on the X axis</param>
  288. /// <param name="ry">Center of the sphere on the Y axis</param>
  289. /// <param name="size">The radius of the sphere</param>
  290. /// <param name="amount">Scale the height of the sphere by this amount (recommended 0..2)</param>
  291. public void raise(double rx, double ry, double size, double amount)
  292. {
  293. lock (heightmap)
  294. {
  295. heightmap.raise(rx, ry, size, amount);
  296. }
  297. tainted++;
  298. }
  299. /// <summary>
  300. /// Lowers the land in a sphere around the specified coordinates
  301. /// </summary>
  302. /// <param name="rx">The center of the sphere at the X axis</param>
  303. /// <param name="ry">The center of the sphere at the Y axis</param>
  304. /// <param name="size">The radius of the sphere in meters</param>
  305. /// <param name="amount">Scale the height of the sphere by this amount (recommended 0..2)</param>
  306. public void lower(double rx, double ry, double size, double amount)
  307. {
  308. lock (heightmap)
  309. {
  310. heightmap.lower(rx, ry, size, amount);
  311. }
  312. tainted++;
  313. }
  314. /// <summary>
  315. /// Generates a simple set of hills in the shape of an island
  316. /// </summary>
  317. public void hills()
  318. {
  319. lock (heightmap)
  320. {
  321. heightmap.hillsSpheres(200, 20, 40, true, true, false);
  322. heightmap.normalise();
  323. heightmap *= 60.0; // Raise to 60m
  324. }
  325. tainted++;
  326. }
  327. /// <summary>
  328. /// Multiplies the heightfield by val
  329. /// </summary>
  330. /// <param name="meep">The heightfield</param>
  331. /// <param name="val">The multiplier</param>
  332. /// <returns></returns>
  333. public static TerrainEngine operator *(TerrainEngine meep, Double val) {
  334. meep.heightmap *= val;
  335. meep.tainted++;
  336. return meep;
  337. }
  338. /// <summary>
  339. /// Returns the height at the coordinates x,y
  340. /// </summary>
  341. /// <param name="x">X Coordinate</param>
  342. /// <param name="y">Y Coordinate</param>
  343. /// <returns></returns>
  344. public float this[int x, int y]
  345. {
  346. get
  347. {
  348. return (float)heightmap.get(x,y);
  349. }
  350. set
  351. {
  352. tainted++;
  353. heightmap.set(x,y,(double)value);
  354. }
  355. }
  356. }
  357. }