TerrainEngine.cs 34 KB

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