TerrainEngine.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  1. /*
  2. * Copyright (c) Contributors, http://www.openmetaverse.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. *
  27. */
  28. using System;
  29. using System.Collections.Generic;
  30. using System.Drawing;
  31. using System.Drawing.Imaging;
  32. using System.IO;
  33. using libTerrain;
  34. using OpenJPEGNet;
  35. namespace OpenSim.Region.Terrain
  36. {
  37. public class TerrainCommand
  38. {
  39. public virtual bool run(string[] cmdargs, ref string output)
  40. {
  41. return false;
  42. }
  43. public string args;
  44. public string help;
  45. }
  46. public class TerrainEngine
  47. {
  48. /// <summary>
  49. /// Plugin library for scripts
  50. /// </summary>
  51. public FilterHost customFilters = new FilterHost();
  52. /// <summary>
  53. /// A [normally] 256x256 heightmap
  54. /// </summary>
  55. public Channel heightmap;
  56. /// <summary>
  57. /// A copy of heightmap at the last save point (for reverting)
  58. /// </summary>
  59. public Channel revertmap;
  60. /// <summary>
  61. /// Water heightmap (needs clientside mods to work)
  62. /// </summary>
  63. public Channel watermap;
  64. /// <summary>
  65. /// Whether or not the terrain has been modified since it was last saved and sent to the Physics engine.
  66. /// Counts the number of modifications since the last save. (0 = Untainted)
  67. /// </summary>
  68. public int tainted;
  69. int w, h;
  70. /// <summary>
  71. /// Generate a new TerrainEngine instance and creates a new heightmap
  72. /// </summary>
  73. public TerrainEngine()
  74. {
  75. w = 256;
  76. h = 256;
  77. heightmap = new Channel(w, h);
  78. tainted++;
  79. }
  80. /// <summary>
  81. /// Converts the heightmap to a 65536 value 1D floating point array
  82. /// </summary>
  83. /// <returns>A float[65536] array containing the heightmap</returns>
  84. public float[] getHeights1D()
  85. {
  86. float[] heights = new float[w * h];
  87. int i;
  88. for (i = 0; i < w * h; i++)
  89. {
  90. heights[i] = (float)heightmap.map[i / w, i % w];
  91. }
  92. return heights;
  93. }
  94. /// <summary>
  95. /// Converts the heightmap to a 256x256 value 2D floating point array.
  96. /// </summary>
  97. /// <returns>An array of 256,256 values containing the heightmap</returns>
  98. public float[,] getHeights2D()
  99. {
  100. float[,] heights = new float[w, h];
  101. int x, y;
  102. for (x = 0; x < w; x++)
  103. {
  104. for (y = 0; y < h; y++)
  105. {
  106. heights[x, y] = (float)heightmap.map[x, y];
  107. }
  108. }
  109. return heights;
  110. }
  111. /// <summary>
  112. /// Imports a 1D floating point array into the 2D heightmap array
  113. /// </summary>
  114. /// <param name="heights">The array to import (must have 65536 members)</param>
  115. public void setHeights1D(float[] heights)
  116. {
  117. int i;
  118. for (i = 0; i < w * h; i++)
  119. {
  120. heightmap.map[i / w, i % w] = heights[i];
  121. }
  122. tainted++;
  123. }
  124. /// <summary>
  125. /// Loads a 2D array of values into the heightmap
  126. /// </summary>
  127. /// <param name="heights">An array of 256,256 float values</param>
  128. public void setHeights2D(float[,] heights)
  129. {
  130. int x, y;
  131. for (x = 0; x < w; x++)
  132. {
  133. for (y = 0; y < h; y++)
  134. {
  135. heightmap.set(x, y, (double)heights[x, y]);
  136. }
  137. }
  138. tainted++;
  139. }
  140. /// <summary>
  141. /// Swaps the two heightmap buffers (the 'revert map' and the heightmap)
  142. /// </summary>
  143. public void swapRevertMaps()
  144. {
  145. Channel backup = heightmap.copy();
  146. heightmap = revertmap;
  147. revertmap = backup;
  148. }
  149. /// <summary>
  150. /// Saves the current heightmap into the revertmap
  151. /// </summary>
  152. public void saveRevertMap()
  153. {
  154. revertmap = heightmap.copy();
  155. }
  156. /// <summary>
  157. /// Processes a terrain-specific command
  158. /// </summary>
  159. /// <param name="args">Commandline arguments (space seperated)</param>
  160. /// <param name="resultText">Reference that returns error or help text if returning false</param>
  161. /// <returns>If the operation was successful (if not, the error is placed into resultText)</returns>
  162. public bool RunTerrainCmd(string[] args, ref string resultText, string simName)
  163. {
  164. string command = args[0];
  165. try
  166. {
  167. switch (command)
  168. {
  169. case "help":
  170. resultText += "terrain regenerate - rebuilds the sims terrain using a default algorithm\n";
  171. resultText += "terrain voronoi <points> <blocksize> - generates a worley fractal with X points per block";
  172. resultText += "terrain seed <seed> - sets the random seed value to <seed>\n";
  173. resultText += "terrain load <type> <filename> - loads a terrain from disk, type can be 'F32', 'F64', 'RAW' or 'IMG'\n";
  174. resultText += "terrain save <type> <filename> - saves a terrain to disk, type can be 'F32', 'F64', 'PNG', 'RAW' or 'HIRAW'\n";
  175. resultText += "terrain save grdmap <filename> <gradient map> - creates a PNG snapshot of the region using a named gradient map\n";
  176. resultText += "terrain rescale <min> <max> - rescales a terrain to be between <min> and <max> meters high\n";
  177. resultText += "terrain erode aerobic <windspeed> <pickupmin> <dropmin> <carry> <rounds> <lowest>\n";
  178. resultText += "terrain erode thermal <talus> <rounds> <carry>\n";
  179. resultText += "terrain multiply <val> - multiplies a terrain by <val>\n";
  180. resultText += "terrain revert - reverts the terrain to the stored original\n";
  181. resultText += "terrain bake - saves the current terrain into the revert map\n";
  182. resultText += "terrain csfilter <filename.cs> - loads a new filter from the specified .cs file\n";
  183. resultText += "terrain jsfilter <filename.js> - loads a new filter from the specified .js file\n";
  184. foreach (KeyValuePair<string, ITerrainFilter> filter in customFilters.filters)
  185. {
  186. resultText += filter.Value.Help();
  187. }
  188. return false;
  189. case "revert":
  190. swapRevertMaps();
  191. saveRevertMap();
  192. break;
  193. case "bake":
  194. saveRevertMap();
  195. break;
  196. case "seed":
  197. setSeed(Convert.ToInt32(args[1]));
  198. break;
  199. case "erode":
  200. return consoleErosion(args, ref resultText);
  201. case "voronoi":
  202. double[] c = new double[2];
  203. c[0] = -1;
  204. c[1] = 1;
  205. heightmap.voronoiDiagram(Convert.ToInt32(args[1]), Convert.ToInt32(args[2]), c);
  206. break;
  207. case "hills":
  208. return consoleHills(args, ref resultText);
  209. case "regenerate":
  210. hills();
  211. break;
  212. case "rescale":
  213. setRange(Convert.ToSingle(args[1]), Convert.ToSingle(args[2]));
  214. break;
  215. case "multiply":
  216. heightmap *= Convert.ToDouble(args[1]);
  217. break;
  218. case "load":
  219. args[2].Replace("%name%", simName);
  220. switch (args[1].ToLower())
  221. {
  222. case "f32":
  223. loadFromFileF32(args[2]);
  224. break;
  225. case "f64":
  226. loadFromFileF64(args[2]);
  227. break;
  228. case "raw":
  229. loadFromFileSLRAW(args[2]);
  230. break;
  231. case "img":
  232. heightmap.loadImage(args[2]);
  233. return false;
  234. default:
  235. resultText = "Unknown image or data format";
  236. return false;
  237. }
  238. break;
  239. case "save":
  240. args[2].Replace("%name%", simName);
  241. switch (args[1].ToLower())
  242. {
  243. case "f32":
  244. writeToFileF32(args[2]);
  245. break;
  246. case "f64":
  247. writeToFileF64(args[2]);
  248. break;
  249. case "grdmap":
  250. exportImage(args[2], args[3]);
  251. break;
  252. case "png":
  253. heightmap.saveImage(args[2]);
  254. break;
  255. case "raw":
  256. writeToFileRAW(args[2]);
  257. break;
  258. case "hiraw":
  259. writeToFileHiRAW(args[2]);
  260. break;
  261. default:
  262. resultText = "Unknown image or data format";
  263. return false;
  264. }
  265. break;
  266. case "csfilter":
  267. customFilters.LoadFilterCSharp(args[1]);
  268. break;
  269. case "jsfilter":
  270. customFilters.LoadFilterJScript(args[1]);
  271. break;
  272. default:
  273. // Run any custom registered filters
  274. if (customFilters.filters.ContainsKey(command))
  275. {
  276. customFilters.filters[command].Filter(heightmap, args);
  277. break;
  278. }
  279. else
  280. {
  281. resultText = "Unknown terrain command";
  282. return false;
  283. }
  284. }
  285. return true;
  286. }
  287. catch (Exception e)
  288. {
  289. resultText = "Error running terrain command: " + e.ToString();
  290. return false;
  291. }
  292. }
  293. private bool consoleErosion(string[] args, ref string resultText)
  294. {
  295. switch (args[1].ToLower())
  296. {
  297. case "aerobic":
  298. // WindSpeed, PickupMinimum,DropMinimum,Carry,Rounds,Lowest
  299. 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]));
  300. break;
  301. case "thermal":
  302. heightmap.thermalWeathering(Convert.ToDouble(args[2]), Convert.ToInt32(args[3]), Convert.ToDouble(args[4]));
  303. break;
  304. default:
  305. resultText = "Unknown erosion type";
  306. return false;
  307. }
  308. return true;
  309. }
  310. private bool consoleHills(string[] args, ref string resultText)
  311. {
  312. int count;
  313. double sizeMin;
  314. double sizeRange;
  315. bool island;
  316. bool additive;
  317. bool noisy;
  318. if (args.GetLength(0) > 2)
  319. {
  320. count = Convert.ToInt32(args[2]);
  321. sizeMin = Convert.ToDouble(args[3]);
  322. sizeRange = Convert.ToDouble(args[4]);
  323. island = Convert.ToBoolean(args[5]);
  324. additive = Convert.ToBoolean(args[6]);
  325. noisy = Convert.ToBoolean(args[7]);
  326. }
  327. else
  328. {
  329. count = 200;
  330. sizeMin = 20;
  331. sizeRange = 40;
  332. island = true;
  333. additive = true;
  334. noisy = false;
  335. }
  336. switch (args[1].ToLower())
  337. {
  338. case "blocks":
  339. heightmap.hillsBlocks(count, sizeMin, sizeRange, island, additive, noisy);
  340. break;
  341. case "cones":
  342. heightmap.hillsCones(count, sizeMin, sizeRange, island, additive, noisy);
  343. break;
  344. case "spheres":
  345. heightmap.hillsSpheres(count, sizeMin, sizeRange, island, additive, noisy);
  346. break;
  347. case "squared":
  348. heightmap.hillsSquared(count, sizeMin, sizeRange, island, additive, noisy);
  349. break;
  350. default:
  351. resultText = "Unknown hills type";
  352. return false;
  353. }
  354. return true;
  355. }
  356. /// <summary>
  357. /// Renormalises the array between min and max
  358. /// </summary>
  359. /// <param name="min">Minimum value of the new array</param>
  360. /// <param name="max">Maximum value of the new array</param>
  361. public void setRange(float min, float max)
  362. {
  363. heightmap.normalise((double)min, (double)max);
  364. tainted++;
  365. }
  366. /// <summary>
  367. /// Loads a file consisting of 256x256 doubles and imports it as an array into the map.
  368. /// </summary>
  369. /// <remarks>TODO: Move this to libTerrain itself</remarks>
  370. /// <param name="filename">The filename of the double array to import</param>
  371. public void loadFromFileF64(string filename)
  372. {
  373. FileInfo file = new FileInfo(filename);
  374. FileStream s = file.Open(FileMode.Open, FileAccess.Read);
  375. BinaryReader bs = new BinaryReader(s);
  376. int x, y;
  377. for (x = 0; x < w; x++)
  378. {
  379. for (y = 0; y < h; y++)
  380. {
  381. heightmap.map[x, y] = bs.ReadDouble();
  382. }
  383. }
  384. bs.Close();
  385. s.Close();
  386. tainted++;
  387. }
  388. /// <summary>
  389. /// Loads a file consisting of 256x256 floats and imports it as an array into the map.
  390. /// </summary>
  391. /// <remarks>TODO: Move this to libTerrain itself</remarks>
  392. /// <param name="filename">The filename of the float array to import</param>
  393. public void loadFromFileF32(string filename)
  394. {
  395. FileInfo file = new FileInfo(filename);
  396. FileStream s = file.Open(FileMode.Open, FileAccess.Read);
  397. BinaryReader bs = new BinaryReader(s);
  398. int x, y;
  399. for (x = 0; x < w; x++)
  400. {
  401. for (y = 0; y < h; y++)
  402. {
  403. heightmap.map[x, y] = (double)bs.ReadSingle();
  404. }
  405. }
  406. bs.Close();
  407. s.Close();
  408. tainted++;
  409. }
  410. /// <summary>
  411. /// Loads a file formatted in the SL .RAW Format used on the main grid
  412. /// </summary>
  413. /// <remarks>This file format stinks and is best avoided.</remarks>
  414. /// <param name="filename">A path to the .RAW format</param>
  415. public void loadFromFileSLRAW(string filename)
  416. {
  417. FileInfo file = new FileInfo(filename);
  418. FileStream s = file.Open(FileMode.Open, FileAccess.Read);
  419. BinaryReader bs = new BinaryReader(s);
  420. int x, y;
  421. for (x = 0; x < w; x++)
  422. {
  423. for (y = 0; y < h; y++)
  424. {
  425. heightmap.map[x, y] = (double)bs.ReadByte() * ((double)bs.ReadByte() / 127.0);
  426. bs.ReadBytes(11); // Advance the stream to next bytes.
  427. }
  428. }
  429. bs.Close();
  430. s.Close();
  431. tainted++;
  432. }
  433. /// <summary>
  434. /// Writes the current terrain heightmap to disk, in the format of a 65536 entry double[] array.
  435. /// </summary>
  436. /// <param name="filename">The desired output filename</param>
  437. public void writeToFileF64(string filename)
  438. {
  439. FileInfo file = new FileInfo(filename);
  440. FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write);
  441. BinaryWriter bs = new BinaryWriter(s);
  442. int x, y;
  443. for (x = 0; x < w; x++)
  444. {
  445. for (y = 0; y < h; y++)
  446. {
  447. bs.Write(heightmap.get(x, y));
  448. }
  449. }
  450. bs.Close();
  451. s.Close();
  452. }
  453. /// <summary>
  454. /// Writes the current terrain heightmap to disk, in the format of a 65536 entry float[] array
  455. /// </summary>
  456. /// <param name="filename">The desired output filename</param>
  457. public void writeToFileF32(string filename)
  458. {
  459. FileInfo file = new FileInfo(filename);
  460. FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write);
  461. BinaryWriter bs = new BinaryWriter(s);
  462. int x, y;
  463. for (x = 0; x < w; x++)
  464. {
  465. for (y = 0; y < h; y++)
  466. {
  467. bs.Write((float)heightmap.get(x, y));
  468. }
  469. }
  470. bs.Close();
  471. s.Close();
  472. }
  473. /// <summary>
  474. /// A very fast LL-RAW file output mechanism - lower precision mechanism but wont take 5 minutes to run either.
  475. /// (is also editable in an image application)
  476. /// </summary>
  477. /// <param name="filename">Filename to write to</param>
  478. public void writeToFileRAW(string filename)
  479. {
  480. FileInfo file = new FileInfo(filename);
  481. FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write);
  482. BinaryWriter bs = new BinaryWriter(s);
  483. int x, y;
  484. // Used for the 'green' channel.
  485. byte avgMultiplier = (byte)heightmap.avg();
  486. byte backupMultiplier = (byte)revertmap.avg();
  487. // Limit the multiplier so it can represent points >64m.
  488. if (avgMultiplier > 196)
  489. avgMultiplier = 196;
  490. if(backupMultiplier > 196)
  491. backupMultiplier = 196;
  492. // Make sure it's at least one to prevent a div by zero
  493. if (avgMultiplier < 1)
  494. avgMultiplier = 1;
  495. if(backupMultiplier < 1)
  496. backupMultiplier = 1;
  497. for (x = 0; x < w; x++)
  498. {
  499. for (y = 0; y < h; y++)
  500. {
  501. byte red = (byte)(heightmap.get(x, y) / ((double)avgMultiplier / 128.0));
  502. byte green = avgMultiplier;
  503. byte blue = (byte)watermap.get(x, y);
  504. byte alpha1 = 0; // Land Parcels
  505. byte alpha2 = 0; // For Sale Land
  506. byte alpha3 = 0; // Public Edit Object
  507. byte alpha4 = 0; // Public Edit Land
  508. byte alpha5 = 255; // Safe Land
  509. byte alpha6 = 255; // Flying Allowed
  510. byte alpha7 = 255; // Create Landmark
  511. byte alpha8 = 255; // Outside Scripts
  512. byte alpha9 = (byte)(revertmap.get(x, y) / ((double)backupMultiplier / 128.0));
  513. byte alpha10 = backupMultiplier;
  514. bs.Write(red);
  515. bs.Write(green);
  516. bs.Write(blue);
  517. bs.Write(alpha1);
  518. bs.Write(alpha2);
  519. bs.Write(alpha3);
  520. bs.Write(alpha4);
  521. bs.Write(alpha5);
  522. bs.Write(alpha6);
  523. bs.Write(alpha7);
  524. bs.Write(alpha8);
  525. bs.Write(alpha9);
  526. bs.Write(alpha10);
  527. }
  528. }
  529. bs.Close();
  530. s.Close();
  531. }
  532. /// <summary>
  533. /// Outputs to a LL compatible RAW in the most efficient manner possible
  534. /// </summary>
  535. /// <remarks>Does not calculate the revert map</remarks>
  536. /// <param name="filename">The filename to output to</param>
  537. public void writeToFileHiRAW(string filename)
  538. {
  539. FileInfo file = new FileInfo(filename);
  540. FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write);
  541. BinaryWriter bs = new BinaryWriter(s);
  542. // Generate a smegging big lookup table to speed the operation up (it needs it)
  543. double[] lookupTable = new double[65536];
  544. int i, j, x, y;
  545. for (i = 0; i < 256; i++)
  546. {
  547. for (j = 0; j < 256; j++)
  548. {
  549. lookupTable[i + (j * 256)] = ((double)i * ((double)j / 127.0));
  550. }
  551. }
  552. // Output the calculated raw
  553. for (x = 0; x < w; x++)
  554. {
  555. for (y = 0; y < h; y++)
  556. {
  557. double t = heightmap.get(x, y);
  558. double min = double.MaxValue;
  559. int index = 0;
  560. for (i = 0; i < 65536; i++)
  561. {
  562. if (Math.Abs(t - lookupTable[i]) < min)
  563. {
  564. min = Math.Abs(t - lookupTable[i]);
  565. index = i;
  566. }
  567. }
  568. byte red = (byte)(index & 0xFF);
  569. byte green = (byte)((index >> 8) & 0xFF);
  570. byte blue = (byte)watermap.get(x, y);
  571. byte alpha1 = 0; // Land Parcels
  572. byte alpha2 = 0; // For Sale Land
  573. byte alpha3 = 0; // Public Edit Object
  574. byte alpha4 = 0; // Public Edit Land
  575. byte alpha5 = 255; // Safe Land
  576. byte alpha6 = 255; // Flying Allowed
  577. byte alpha7 = 255; // Create Landmark
  578. byte alpha8 = 255; // Outside Scripts
  579. byte alpha9 = red;
  580. byte alpha10 = green;
  581. bs.Write(red);
  582. bs.Write(green);
  583. bs.Write(blue);
  584. bs.Write(alpha1);
  585. bs.Write(alpha2);
  586. bs.Write(alpha3);
  587. bs.Write(alpha4);
  588. bs.Write(alpha5);
  589. bs.Write(alpha6);
  590. bs.Write(alpha7);
  591. bs.Write(alpha8);
  592. bs.Write(alpha9);
  593. bs.Write(alpha10);
  594. }
  595. }
  596. bs.Close();
  597. s.Close();
  598. }
  599. /// <summary>
  600. /// Sets the random seed to be used by procedural functions which involve random numbers.
  601. /// </summary>
  602. /// <param name="val">The desired seed</param>
  603. public void setSeed(int val)
  604. {
  605. heightmap.seed = val;
  606. }
  607. /// <summary>
  608. /// Raises land in a sphere around the specified coordinates
  609. /// </summary>
  610. /// <param name="rx">Center of the sphere on the X axis</param>
  611. /// <param name="ry">Center of the sphere on the Y axis</param>
  612. /// <param name="size">The radius of the sphere</param>
  613. /// <param name="amount">Scale the height of the sphere by this amount (recommended 0..2)</param>
  614. public void raise(double rx, double ry, double size, double amount)
  615. {
  616. lock (heightmap)
  617. {
  618. heightmap.raise(rx, ry, size, amount);
  619. }
  620. tainted++;
  621. }
  622. /// <summary>
  623. /// Lowers the land in a sphere around the specified coordinates
  624. /// </summary>
  625. /// <param name="rx">The center of the sphere at the X axis</param>
  626. /// <param name="ry">The center of the sphere at the Y axis</param>
  627. /// <param name="size">The radius of the sphere in meters</param>
  628. /// <param name="amount">Scale the height of the sphere by this amount (recommended 0..2)</param>
  629. public void lower(double rx, double ry, double size, double amount)
  630. {
  631. lock (heightmap)
  632. {
  633. heightmap.lower(rx, ry, size, amount);
  634. }
  635. tainted++;
  636. }
  637. /// <summary>
  638. /// Flattens the land under the brush of specified coordinates (spherical mask)
  639. /// </summary>
  640. /// <param name="rx">Center of sphere</param>
  641. /// <param name="ry">Center of sphere</param>
  642. /// <param name="size">Radius of the sphere</param>
  643. /// <param name="amount">Thickness of the mask (0..2 recommended)</param>
  644. public void flatten(double rx, double ry, double size, double amount)
  645. {
  646. lock (heightmap)
  647. {
  648. heightmap.flatten(rx, ry, size, amount);
  649. }
  650. tainted++;
  651. }
  652. /// <summary>
  653. /// Creates noise within the specified bounds
  654. /// </summary>
  655. /// <param name="rx">Center of the bounding sphere</param>
  656. /// <param name="ry">Center of the bounding sphere</param>
  657. /// <param name="size">The radius of the sphere</param>
  658. /// <param name="amount">Strength of the mask (0..2) recommended</param>
  659. public void noise(double rx, double ry, double size, double amount)
  660. {
  661. lock (heightmap)
  662. {
  663. Channel smoothed = new Channel();
  664. smoothed.noise();
  665. Channel mask = new Channel();
  666. mask.raise(rx, ry, size, amount);
  667. heightmap.blend(smoothed, mask);
  668. }
  669. tainted++;
  670. }
  671. /// <summary>
  672. /// Reverts land within the specified bounds
  673. /// </summary>
  674. /// <param name="rx">Center of the bounding sphere</param>
  675. /// <param name="ry">Center of the bounding sphere</param>
  676. /// <param name="size">The radius of the sphere</param>
  677. /// <param name="amount">Strength of the mask (0..2) recommended</param>
  678. public void revert(double rx, double ry, double size, double amount)
  679. {
  680. lock (heightmap)
  681. {
  682. Channel mask = new Channel();
  683. mask.raise(rx, ry, size, amount);
  684. heightmap.blend(revertmap, mask);
  685. }
  686. tainted++;
  687. }
  688. /// <summary>
  689. /// Smooths land under the brush of specified coordinates (spherical mask)
  690. /// </summary>
  691. /// <param name="rx">Center of the sphere</param>
  692. /// <param name="ry">Center of the sphere</param>
  693. /// <param name="size">Radius of the sphere</param>
  694. /// <param name="amount">Thickness of the mask (0..2 recommended)</param>
  695. public void smooth(double rx, double ry, double size, double amount)
  696. {
  697. lock (heightmap)
  698. {
  699. Channel smoothed = heightmap.copy();
  700. smoothed.smooth(amount);
  701. Channel mask = new Channel();
  702. mask.raise(rx,ry,size,amount);
  703. heightmap.blend(smoothed, mask);
  704. }
  705. tainted++;
  706. }
  707. /// <summary>
  708. /// Generates a simple set of hills in the shape of an island
  709. /// </summary>
  710. public void hills()
  711. {
  712. lock (heightmap)
  713. {
  714. heightmap.hillsSpheres(200, 20, 40, true, true, false);
  715. heightmap.normalise();
  716. heightmap *= 60.0; // Raise to 60m
  717. }
  718. tainted++;
  719. }
  720. /// <summary>
  721. /// Wrapper to heightmap.get()
  722. /// </summary>
  723. /// <param name="x">X coord</param>
  724. /// <param name="y">Y coord</param>
  725. /// <returns>Height at specified coordinates</returns>
  726. public double get(int x, int y)
  727. {
  728. return heightmap.get(x, y);
  729. }
  730. /// <summary>
  731. /// Multiplies the heightfield by val
  732. /// </summary>
  733. /// <param name="meep">The heightfield</param>
  734. /// <param name="val">The multiplier</param>
  735. /// <returns></returns>
  736. public static TerrainEngine operator *(TerrainEngine meep, Double val)
  737. {
  738. meep.heightmap *= val;
  739. meep.tainted++;
  740. return meep;
  741. }
  742. /// <summary>
  743. /// Exports the current heightmap to a PNG file
  744. /// </summary>
  745. /// <param name="filename">The destination filename for the image</param>
  746. /// <param name="gradientmap">A 1x*height* image which contains the colour gradient to export with. Must be at least 1x2 pixels, 1x256 or more is ideal.</param>
  747. public void exportImage(string filename, string gradientmap)
  748. {
  749. try
  750. {
  751. Bitmap gradientmapLd = new Bitmap(gradientmap);
  752. int pallete = gradientmapLd.Height;
  753. Bitmap bmp = new Bitmap(heightmap.w, heightmap.h);
  754. Color[] colours = new Color[pallete];
  755. for (int i = 0; i < pallete; i++)
  756. {
  757. colours[i] = gradientmapLd.GetPixel(0, i);
  758. }
  759. Channel copy = heightmap.copy();
  760. for (int x = 0; x < copy.w; x++)
  761. {
  762. for (int y = 0; y < copy.h; y++)
  763. {
  764. // 512 is the largest possible height before colours clamp
  765. int colorindex = (int)(Math.Max(Math.Min(1.0, copy.get(x, y) / 512.0), 0.0) * pallete);
  766. bmp.SetPixel(x, y, colours[colorindex]);
  767. }
  768. }
  769. bmp.Save(filename, ImageFormat.Png);
  770. }
  771. catch (Exception e)
  772. {
  773. Console.WriteLine("Failed generating terrain map: " + e.ToString());
  774. }
  775. }
  776. /// <summary>
  777. /// Exports the current heightmap in Jpeg2000 format to a byte[]
  778. /// </summary>
  779. /// <param name="gradientmap">A 1x*height* image which contains the colour gradient to export with. Must be at least 1x2 pixels, 1x256 or more is ideal.</param>
  780. public byte[] exportJpegImage(string gradientmap)
  781. {
  782. byte[] imageData = null;
  783. try
  784. {
  785. Bitmap gradientmapLd = new Bitmap(gradientmap);
  786. int pallete = gradientmapLd.Height;
  787. Bitmap bmp = new Bitmap(heightmap.w, heightmap.h);
  788. Color[] colours = new Color[pallete];
  789. for (int i = 0; i < pallete; i++)
  790. {
  791. colours[i] = gradientmapLd.GetPixel(0, i);
  792. }
  793. Channel copy = heightmap.copy();
  794. for (int x = 0; x < copy.w; x++)
  795. {
  796. for (int y = 0; y < copy.h; y++)
  797. {
  798. // 512 is the largest possible height before colours clamp
  799. int colorindex = (int)(Math.Max(Math.Min(1.0, copy.get(copy.h - y, x) / 512.0), 0.0) * pallete);
  800. bmp.SetPixel(x, y, colours[colorindex]);
  801. }
  802. }
  803. //bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
  804. imageData = OpenJPEG.EncodeFromImage(bmp, "map");
  805. }
  806. catch (Exception e)
  807. {
  808. Console.WriteLine("Failed generating terrain map: " + e.ToString());
  809. }
  810. return imageData;
  811. }
  812. }
  813. }