TerrainEngine.cs 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.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. using System;
  28. using System.Collections.Generic;
  29. using System.Drawing;
  30. using System.Drawing.Imaging;
  31. using System.Globalization;
  32. using System.IO;
  33. using System.Threading;
  34. using libTerrain;
  35. using OpenJPEGNet;
  36. using OpenSim.Framework;
  37. namespace OpenSim.Region.Terrain
  38. {
  39. public class TerrainCommand
  40. {
  41. public virtual bool run(string[] cmdargs, ref string output)
  42. {
  43. return false;
  44. }
  45. public string args;
  46. public string help;
  47. }
  48. public class TerrainEngine
  49. {
  50. public static Mutex fileIOLock = new Mutex();
  51. /// <summary>
  52. /// Plugin library for scripts
  53. /// </summary>
  54. public FilterHost customFilters = new FilterHost();
  55. /// <summary>
  56. /// A [normally] 256x256 heightmap
  57. /// </summary>
  58. public Channel heightmap;
  59. /// <summary>
  60. /// A copy of heightmap at the last save point (for reverting)
  61. /// </summary>
  62. public Channel revertmap;
  63. /// <summary>
  64. /// Water heightmap (needs clientside mods to work)
  65. /// </summary>
  66. public Channel watermap;
  67. /// <summary>
  68. /// Max amount the terrain can be raised from the revert parameters
  69. /// </summary>
  70. public double maxRaise = 500.0;
  71. /// <summary>
  72. /// Min amount the terrain can be lowered from the revert parameters
  73. /// </summary>
  74. public double minLower = 500.0;
  75. /// <summary>
  76. /// The last time the terrain was edited
  77. /// </summary>
  78. public DateTime lastEdit = DateTime.Now;
  79. private int counter = 0;
  80. /// <summary>
  81. /// Whether or not the terrain has been modified since it was last saved and sent to the Physics engine.
  82. /// Counts the number of modifications since the last save. (0 = Untainted)
  83. /// </summary>
  84. public int tainted;
  85. private int w, h;
  86. /// <summary>
  87. /// Used to determine what offset to use when loading singular heightmaps across multiple sims
  88. /// </summary>
  89. private int offsetX;
  90. private int offsetY;
  91. /// <summary>
  92. /// Generate a new TerrainEngine instance and creates a new heightmap
  93. /// </summary>
  94. public TerrainEngine(int X, int Y)
  95. {
  96. w = 256;
  97. h = 256;
  98. heightmap = new Channel(w, h);
  99. revertmap = new Channel(w, h);
  100. watermap = new Channel(w, h);
  101. watermap.Fill(20);
  102. offsetX = X;
  103. offsetY = Y;
  104. tainted++;
  105. }
  106. public bool IsTainted()
  107. {
  108. return (tainted != 0);
  109. }
  110. public bool IsUserStillEditing()
  111. {
  112. TimeSpan gap = DateTime.Now - lastEdit;
  113. if (gap.TotalSeconds <= 4.0)
  114. return true;
  115. return false;
  116. }
  117. public bool IsTainted(int x, int y)
  118. {
  119. return (heightmap.diff[x/16, y/16] != 0);
  120. }
  121. public void ResetTaint()
  122. {
  123. tainted = 0;
  124. heightmap.diff = new int[w/16,h/16];
  125. }
  126. //Testing to see if moving the TerraForming packet handling code into here works well
  127. /// <summary>
  128. /// Modifies terrain using the specified information
  129. /// </summary>
  130. /// <param name="height">The height at which the user started modifying the terrain</param>
  131. /// <param name="seconds">The number of seconds the modify button was pressed</param>
  132. /// <param name="brushsize">The size of the brush used</param>
  133. /// <param name="action">The action to be performed</param>
  134. /// <param name="north">Distance from the north border where the cursor is located</param>
  135. /// <param name="west">Distance from the west border where the cursor is located</param>
  136. public void ModifyTerrain(float height, float seconds, byte brushsize, byte action, float north, float west,
  137. float south, float east,
  138. IClientAPI remoteUser)
  139. {
  140. // Shiny.
  141. double size = (double) (1 << brushsize);
  142. //System.Console.WriteLine("SIZE:" + size.ToString() + " Seconds:" + seconds.ToString());
  143. if (seconds == 1)
  144. {
  145. seconds = 0.0315f;
  146. }
  147. /* Okay, so here's the deal
  148. * This has to handle both when a user draws on the terrain *and* when a user selects
  149. * a selection of AABB on terrain and applies whatever routine the client requests
  150. * There's something currently wrong with the brushsize --> size conversion.. however
  151. * it's workable.. just unpredictable.
  152. *
  153. * North is always higher and East is always higher
  154. * in the AABB representation
  155. *
  156. * Therefore what we're doing is looping from south to north and west to east
  157. * and applying the associated algorithm with the brush.
  158. *
  159. * This works good on the fast ones, but things like smooth take 12 seconds a single click..
  160. * for now, smooth won't be 'selectionated'
  161. *
  162. * If the user draws instead of selects, north will = south, and east will = west.
  163. * if the user selects, then the selection is inclusive
  164. * it'll always affect at least one point on the heightmap.
  165. *
  166. * that means we use the <= operator
  167. *
  168. * Again, libTerrain is yx instead of xy.. so, it's reflected in the function calls
  169. *
  170. */
  171. switch (action)
  172. {
  173. case 0:
  174. // flatten terrain
  175. for (float x = south; x <= north; x++)
  176. {
  177. for (float y = west; y <= east; y++)
  178. {
  179. FlattenTerrain(y, x, size, (double) seconds/5.0);
  180. lastEdit = DateTime.Now;
  181. //remoteUser.SendLayerData((int)(x / 16), (int)(x / 16), GetHeights1D());
  182. }
  183. }
  184. break;
  185. case 1:
  186. // raise terrain
  187. for (float x = south; x <= north; x++)
  188. {
  189. for (float y = west; y <= east; y++)
  190. {
  191. RaiseTerrain(y, x, size, (double) seconds/5.0);
  192. lastEdit = DateTime.Now;
  193. //remoteUser.SendLayerData((int)(x / 16), (int)(x / 16), GetHeights1D());
  194. }
  195. }
  196. break;
  197. case 2:
  198. //lower terrain
  199. for (float x = south; x <= north; x++)
  200. {
  201. for (float y = west; y <= east; y++)
  202. {
  203. LowerTerrain(y, x, size, (double) seconds/5.0);
  204. lastEdit = DateTime.Now;
  205. //remoteUser.SendLayerData((int)(x / 16), (int)(x / 16), GetHeights1D());
  206. }
  207. }
  208. break;
  209. case 3:
  210. // smooth terrain
  211. //
  212. // We're leaving this out of the parcel calculations for now
  213. // because just a single one of these will stall your sim for
  214. // 12 seconds. Looping over the parcel on this one is just stupid
  215. //
  216. //for (float x = south; x <= north; x++)
  217. //{
  218. //for (float y = west; y <= east; y++)
  219. //{
  220. //SmoothTerrain(y, x , size, (double)seconds / 5.0);
  221. //}
  222. //}
  223. SmoothTerrain(west, north, size, (double) seconds/5.0);
  224. break;
  225. case 4:
  226. // noise
  227. for (float x = south; x <= north; x++)
  228. {
  229. for (float y = west; y <= east; y++)
  230. {
  231. NoiseTerrain(y, x, size, (double) seconds/5.0);
  232. lastEdit = DateTime.Now;
  233. }
  234. }
  235. break;
  236. case 5:
  237. // revert
  238. for (float x = south; x <= north; x++)
  239. {
  240. for (float y = west; y <= east; y++)
  241. {
  242. RevertTerrain(y, x, size, (double) seconds/5.0);
  243. lastEdit = DateTime.Now;
  244. }
  245. }
  246. break;
  247. // CLIENT EXTENSIONS GO HERE
  248. case 128:
  249. // erode-thermal
  250. break;
  251. case 129:
  252. // erode-aerobic
  253. break;
  254. case 130:
  255. // erode-hydraulic
  256. break;
  257. }
  258. counter++;
  259. if(counter==2)
  260. {
  261. counter=0;
  262. for (int x = 0; x < 16; x++)
  263. {
  264. for (int y = 0; y < 16; y++)
  265. {
  266. if (IsTainted(x * 16, y * 16))
  267. {
  268. bool usingTerrainModule = true;
  269. if (!usingTerrainModule)
  270. {
  271. remoteUser.SendLayerData(x, y, GetHeights1D());
  272. }
  273. }
  274. }
  275. }
  276. }
  277. lastEdit = DateTime.Now;
  278. return;
  279. }
  280. // TODO: unused
  281. // /// <summary>
  282. // /// Checks to make sure the terrain is within baked values +/- maxRaise/minLower
  283. // /// </summary>
  284. // private void SetTerrainWithinBounds()
  285. // {
  286. // int x, y;
  287. // for (x = 0; x < w; x++)
  288. // {
  289. // for (y = 0; y < h; y++)
  290. // {
  291. // if ((heightmap.Get(x, y) > revertmap.Get(x, y) + maxRaise))
  292. // {
  293. // heightmap.map[x, y] = revertmap.Get(x, y) + maxRaise;
  294. // }
  295. // if ((heightmap.Get(x, y) > revertmap.Get(x, y) - minLower))
  296. // {
  297. // heightmap.map[x, y] = revertmap.Get(x, y) - minLower;
  298. // }
  299. // }
  300. // }
  301. // }
  302. /// <summary>
  303. /// Converts the heightmap to a 65536 value 1D floating point array
  304. /// </summary>
  305. /// <returns>A float[65536] array containing the heightmap</returns>
  306. public float[] GetHeights1D()
  307. {
  308. float[] heights = new float[w*h];
  309. int i;
  310. for (i = 0; i < w*h; i++)
  311. {
  312. heights[i] = (float) heightmap.map[i%w, i/w];
  313. }
  314. return heights;
  315. }
  316. /// <summary>
  317. /// Converts the heightmap to a 256x256 value 2D floating point array.
  318. /// </summary>
  319. /// <returns>An array of 256,256 values containing the heightmap</returns>
  320. public float[,] GetHeights2D()
  321. {
  322. float[,] heights = new float[w,h];
  323. int x, y;
  324. for (x = 0; x < w; x++)
  325. {
  326. for (y = 0; y < h; y++)
  327. {
  328. heights[x, y] = (float) heightmap.map[x, y];
  329. }
  330. }
  331. return heights;
  332. }
  333. /// <summary>
  334. /// Converts the heightmap to a 256x256 value 2D floating point array. Double precision version.
  335. /// </summary>
  336. /// <returns>An array of 256,256 values containing the heightmap</returns>
  337. public double[,] GetHeights2DD()
  338. {
  339. return heightmap.map;
  340. }
  341. /// <summary>
  342. /// Imports a 1D floating point array into the 2D heightmap array
  343. /// </summary>
  344. /// <param name="heights">The array to import (must have 65536 members)</param>
  345. public void GetHeights1D(float[] heights)
  346. {
  347. int i;
  348. for (i = 0; i < w*h; i++)
  349. {
  350. heightmap.map[i%w, i/w] = heights[i];
  351. }
  352. tainted++;
  353. }
  354. /// <summary>
  355. /// Loads a 2D array of values into the heightmap
  356. /// </summary>
  357. /// <param name="heights">An array of 256,256 float values</param>
  358. public void SetHeights2D(float[,] heights)
  359. {
  360. int x, y;
  361. for (x = 0; x < w; x++)
  362. {
  363. for (y = 0; y < h; y++)
  364. {
  365. heightmap.Set(x, y, (double) heights[x, y]);
  366. }
  367. }
  368. SaveRevertMap();
  369. tainted++;
  370. }
  371. /// <summary>
  372. /// Loads a 2D array of values into the heightmap (Double Precision Version)
  373. /// </summary>
  374. /// <param name="heights">An array of 256,256 float values</param>
  375. public void SetHeights2D(double[,] heights)
  376. {
  377. int x, y;
  378. for (x = 0; x < w; x++)
  379. {
  380. for (y = 0; y < h; y++)
  381. {
  382. heightmap.Set(x, y, heights[x, y]);
  383. }
  384. }
  385. SaveRevertMap();
  386. ResetTaint();
  387. }
  388. /// <summary>
  389. /// Swaps the two heightmap buffers (the 'revert map' and the heightmap)
  390. /// </summary>
  391. public void SwapRevertMaps()
  392. {
  393. Channel backup = heightmap.Copy();
  394. heightmap = revertmap;
  395. revertmap = backup;
  396. }
  397. /// <summary>
  398. /// Saves the current heightmap into the revertmap
  399. /// </summary>
  400. public void SaveRevertMap()
  401. {
  402. revertmap = heightmap.Copy();
  403. }
  404. /// <summary>
  405. /// Processes a terrain-specific command
  406. /// </summary>
  407. /// <param name="args">Commandline arguments (space seperated)</param>
  408. /// <param name="resultText">Reference that returns error or help text if returning false</param>
  409. /// <returns>If the operation was successful (if not, the error is placed into resultText)</returns>
  410. public bool RunTerrainCmd(string[] args, ref string resultText, string simName)
  411. {
  412. string command;
  413. if (args.Length > 0)
  414. {
  415. command = args[0];
  416. }
  417. else
  418. {
  419. command = "help";
  420. }
  421. try
  422. {
  423. switch (command)
  424. {
  425. case "help":
  426. resultText += "terrain regenerate - rebuilds the sims terrain using a default algorithm\n";
  427. resultText +=
  428. "terrain hills <type> <number of hills> <min height> <max height> <island t/f> <additive t/f> <noisy t/f>\n";
  429. resultText += " type should be spheres, blocks, cones, or squared\n";
  430. resultText +=
  431. "terrain voronoi <points> <blocksize> - generates a worley fractal with X points per block";
  432. resultText += "terrain seed <seed> - sets the random seed value to <seed>\n";
  433. resultText +=
  434. "terrain load <type> <filename> - loads a terrain from disk, type can be 'F32', 'F64', 'RAW' or 'IMG'\n";
  435. resultText +=
  436. "terrain save <type> <filename> - saves a terrain to disk, type can be 'F32', 'F64', 'PNG', 'RAW' or 'HIRAW'\n";
  437. resultText +=
  438. "terrain save grdmap <filename> <gradient map> - creates a PNG snapshot of the region using a named gradient map\n";
  439. resultText +=
  440. "terrain rescale <min> <max> - rescales a terrain to be between <min> and <max> meters high\n";
  441. resultText += "terrain fill <val> - fills a terrain at the specified height\n";
  442. resultText +=
  443. "terrain erode aerobic <windspeed> <pickupmin> <dropmin> <carry> <rounds> <lowest t/f> <fluid dynamics t/f>\n";
  444. resultText += "terrain erode thermal <talus> <rounds> <carry>\n";
  445. resultText += "terrain erode hydraulic <rain> <evaporation> <solubility> <frequency> <rounds>\n";
  446. resultText += "terrain multiply <val> - multiplies a terrain by <val>\n";
  447. resultText += "terrain elevate <val> - elevates a terrain by <val>\n";
  448. resultText += "terrain revert - reverts the terrain to the stored original\n";
  449. resultText += "terrain bake - saves the current terrain into the revert map\n";
  450. resultText +=
  451. "terrain csfilter <filename.cs> - loads a new filter from the specified .cs file\n";
  452. resultText +=
  453. "terrain jsfilter <filename.js> - loads a new filter from the specified .js file\n";
  454. foreach (KeyValuePair<string, ITerrainFilter> filter in customFilters.filters)
  455. {
  456. resultText += filter.Value.Help();
  457. }
  458. return false;
  459. case "revert":
  460. SwapRevertMaps();
  461. SaveRevertMap();
  462. break;
  463. case "bake":
  464. SaveRevertMap();
  465. break;
  466. case "seed":
  467. SetSeed(Convert.ToInt32(args[1]));
  468. break;
  469. case "erode":
  470. return ConsoleErosion(args, ref resultText);
  471. case "voronoi":
  472. double[] c = new double[2];
  473. c[0] = -1;
  474. c[1] = 1;
  475. heightmap.VoronoiDiagram(Convert.ToInt32(args[1]), Convert.ToInt32(args[2]), c);
  476. break;
  477. case "hills":
  478. return ConsoleHills(args, ref resultText);
  479. case "regenerate":
  480. SetDefaultTerrain();
  481. break;
  482. case "rescale":
  483. SetRange(Convert.ToSingle(args[1]), Convert.ToSingle(args[2]));
  484. break;
  485. case "elevate":
  486. Elevate(Convert.ToSingle(args[1]));
  487. break;
  488. case "fill":
  489. heightmap.Fill(Convert.ToDouble(args[1]));
  490. tainted++;
  491. break;
  492. case "clip":
  493. heightmap.Clip(Convert.ToDouble(args[1]), Convert.ToDouble(args[2]));
  494. tainted++;
  495. break;
  496. case "smooth":
  497. heightmap.Smooth(Convert.ToDouble(args[1]));
  498. tainted++;
  499. break;
  500. case "add":
  501. heightmap += Convert.ToDouble(args[1]);
  502. tainted++;
  503. break;
  504. case "multiply":
  505. heightmap *= Convert.ToDouble(args[1]);
  506. tainted++;
  507. break;
  508. case "load":
  509. string filenameL = args[2].Replace("%name%", simName);
  510. filenameL = filenameL.Replace("%x%", offsetX.ToString());
  511. filenameL = filenameL.Replace("%y%", offsetY.ToString());
  512. switch (args[1].ToLower())
  513. {
  514. case "f32":
  515. LoadFromFileF32(filenameL);
  516. break;
  517. case "f64":
  518. LoadFromFileF64(filenameL);
  519. break;
  520. case "raw":
  521. LoadFromFileSLRAW(filenameL);
  522. break;
  523. case "img":
  524. heightmap = heightmap.LoadImage(filenameL);
  525. tainted++;
  526. break;
  527. default:
  528. resultText = "Unknown image or data format";
  529. return false;
  530. }
  531. break;
  532. case "load-tile":
  533. switch (args[1].ToLower())
  534. {
  535. case "f32":
  536. LoadFromFileF32(args[2], Convert.ToInt32(args[3]), Convert.ToInt32(args[4]),
  537. Convert.ToInt32(args[5]), Convert.ToInt32(args[6]));
  538. break;
  539. case "raw":
  540. LoadFromFileSLRAW(args[2], Convert.ToInt32(args[3]), Convert.ToInt32(args[4]),
  541. Convert.ToInt32(args[5]), Convert.ToInt32(args[6]));
  542. break;
  543. case "img":
  544. LoadFromFileIMG(args[2], Convert.ToInt32(args[3]), Convert.ToInt32(args[4]),
  545. Convert.ToInt32(args[5]), Convert.ToInt32(args[6]));
  546. break;
  547. default:
  548. resultText = "Unknown or unsupported image or data format";
  549. return false;
  550. }
  551. break;
  552. case "save":
  553. string filename = args[2].Replace("%name%", simName);
  554. filename = filename.Replace("%x%", offsetX.ToString());
  555. filename = filename.Replace("%y%", offsetY.ToString());
  556. switch (args[1].ToLower())
  557. {
  558. case "f32":
  559. WriteToFileF32(filename);
  560. break;
  561. case "f64":
  562. WriteToFileF64(filename);
  563. break;
  564. case "grdmap":
  565. if (args.Length >= 4)
  566. WriteImage(filename, args[3]);
  567. else
  568. WriteImage(filename, "defaultstripe.png");
  569. break;
  570. case "png":
  571. heightmap.SaveImage(filename);
  572. break;
  573. case "raw":
  574. WriteToFileRAW(filename);
  575. break;
  576. case "hiraw":
  577. WriteToFileHiRAW(filename);
  578. break;
  579. default:
  580. resultText = "Unknown image or data format";
  581. return false;
  582. }
  583. break;
  584. case "csfilter":
  585. customFilters.LoadFilterCSharp(args[1]);
  586. break;
  587. case "jsfilter":
  588. customFilters.LoadFilterJScript(args[1]);
  589. break;
  590. default:
  591. // Run any custom registered filters
  592. if (customFilters.filters.ContainsKey(command))
  593. {
  594. customFilters.filters[command].Filter(heightmap, args);
  595. break;
  596. }
  597. else
  598. {
  599. resultText = "Unknown terrain command";
  600. return false;
  601. }
  602. }
  603. return true;
  604. }
  605. catch (Exception e) // SEMI-LEGIT: Catching problems caused by user input or scripts
  606. {
  607. resultText = "Error running terrain command: " + e.ToString();
  608. return false;
  609. }
  610. }
  611. private bool ConsoleErosion(string[] args, ref string resultText)
  612. {
  613. double min = heightmap.FindMin();
  614. double max = heightmap.FindMax();
  615. switch (args[1].ToLower())
  616. {
  617. case "aerobic":
  618. // WindSpeed, PickupMinimum,DropMinimum,Carry,Rounds,Lowest
  619. heightmap.AerobicErosion(Convert.ToDouble(args[2]), Convert.ToDouble(args[3]),
  620. Convert.ToDouble(args[4]), Convert.ToDouble(args[5]),
  621. Convert.ToInt32(args[6]), Convert.ToBoolean(args[7]),
  622. Convert.ToBoolean(args[8]));
  623. break;
  624. case "thermal":
  625. heightmap.ThermalWeathering(Convert.ToDouble(args[2]), Convert.ToInt32(args[3]),
  626. Convert.ToDouble(args[4]));
  627. break;
  628. case "hydraulic":
  629. Channel rainMap = new Channel(w, h);
  630. rainMap.Fill(Convert.ToDouble(args[2]));
  631. heightmap.HydraulicErosion(rainMap, Convert.ToDouble(args[3]), Convert.ToDouble(args[4]),
  632. Convert.ToInt32(args[5]), Convert.ToInt32(args[6]));
  633. break;
  634. default:
  635. resultText = "Unknown erosion type";
  636. return false;
  637. }
  638. heightmap.Normalise(min, max);
  639. tainted++;
  640. return true;
  641. }
  642. private bool ConsoleHills(string[] args, ref string resultText)
  643. {
  644. Random RandomClass = new Random();
  645. SetSeed(RandomClass.Next());
  646. int count;
  647. double sizeMin;
  648. double sizeRange;
  649. bool island;
  650. bool additive;
  651. bool noisy;
  652. if (args.GetLength(0) > 2)
  653. {
  654. int.TryParse(args[2].ToString(), out count);
  655. double.TryParse(args[3].ToString(), NumberStyles.AllowDecimalPoint, Culture.NumberFormatInfo,
  656. out sizeMin);
  657. double.TryParse(args[4].ToString(), NumberStyles.AllowDecimalPoint, Culture.NumberFormatInfo,
  658. out sizeRange);
  659. bool.TryParse(args[5].ToString(), out island);
  660. bool.TryParse(args[6].ToString(), out additive);
  661. bool.TryParse(args[7].ToString(), out noisy);
  662. }
  663. else
  664. {
  665. count = 200;
  666. sizeMin = 20;
  667. sizeRange = 40;
  668. island = true;
  669. additive = true;
  670. noisy = false;
  671. }
  672. switch (args[1].ToLower())
  673. {
  674. case "blocks":
  675. heightmap.HillsBlocks(count, sizeMin, sizeRange, island, additive, noisy);
  676. break;
  677. case "cones":
  678. heightmap.HillsCones(count, sizeMin, sizeRange, island, additive, noisy);
  679. break;
  680. case "spheres":
  681. heightmap.HillsSpheres(count, sizeMin, sizeRange, island, additive, noisy);
  682. break;
  683. case "squared":
  684. heightmap.HillsSquared(count, sizeMin, sizeRange, island, additive, noisy);
  685. break;
  686. default:
  687. resultText = "Unknown hills type";
  688. return false;
  689. }
  690. tainted++;
  691. return true;
  692. }
  693. /// <summary>
  694. /// Renormalises the array between min and max
  695. /// </summary>
  696. /// <param name="min">Minimum value of the new array</param>
  697. /// <param name="max">Maximum value of the new array</param>
  698. public void SetRange(float min, float max)
  699. {
  700. heightmap.Normalise((double) min, (double) max);
  701. tainted++;
  702. }
  703. /// <summary>
  704. /// Adds meters (positive or negative) to terrain height
  705. /// </summary>
  706. /// <param name="meters">Positive or negative value to add to new array</param>
  707. public void Elevate(float meters)
  708. {
  709. heightmap.Elevate((double)meters);
  710. tainted++;
  711. }
  712. /// <summary>
  713. /// Loads a file consisting of 256x256 doubles and imports it as an array into the map.
  714. /// </summary>
  715. /// <remarks>TODO: Move this to libTerrain itself</remarks>
  716. /// <param name="filename">The filename of the double array to import</param>
  717. public void LoadFromFileF64(string filename)
  718. {
  719. FileInfo file = new FileInfo(filename);
  720. FileStream s = file.Open(FileMode.Open, FileAccess.Read);
  721. BinaryReader bs = new BinaryReader(s);
  722. int x, y;
  723. for (y = 0; y < h; y++)
  724. {
  725. for (x = 0; x < h; x++)
  726. {
  727. heightmap.Set(x, y, (double) bs.ReadSingle());
  728. }
  729. }
  730. bs.Close();
  731. s.Close();
  732. tainted++;
  733. }
  734. /// <summary>
  735. /// Loads a file consisting of 256x256 floats and imports it as an array into the map.
  736. /// </summary>
  737. /// <remarks>TODO: Move this to libTerrain itself</remarks>
  738. /// <param name="filename">The filename of the float array to import</param>
  739. public void LoadFromFileF32(string filename)
  740. {
  741. FileInfo file = new FileInfo(filename);
  742. FileStream s = file.Open(FileMode.Open, FileAccess.Read);
  743. BinaryReader bs = new BinaryReader(s);
  744. int x, y;
  745. for (y = 0; y < h; y++)
  746. {
  747. for (x = 0; x < w; x++)
  748. {
  749. heightmap.Set(x, y, (double) bs.ReadSingle());
  750. }
  751. }
  752. bs.Close();
  753. s.Close();
  754. tainted++;
  755. }
  756. /// <summary>
  757. /// Loads a section of a larger heightmap (F32)
  758. /// </summary>
  759. /// <param name="filename">File to load</param>
  760. /// <param name="dimensionX">Size of the file</param>
  761. /// <param name="dimensionY">Size of the file</param>
  762. /// <param name="lowerboundX">Where do the region coords start for this terrain?</param>
  763. /// <param name="lowerboundY">Where do the region coords start for this terrain?</param>
  764. public void LoadFromFileF32(string filename, int dimensionX, int dimensionY, int lowerboundX, int lowerboundY)
  765. {
  766. fileIOLock.WaitOne();
  767. try
  768. {
  769. int sectionToLoadX = ((offsetX - lowerboundX)*w);
  770. int sectionToLoadY = ((offsetY - lowerboundY)*h);
  771. double[,] tempMap = new double[dimensionX,dimensionY];
  772. FileInfo file = new FileInfo(filename);
  773. FileStream s = file.Open(FileMode.Open, FileAccess.Read);
  774. BinaryReader bs = new BinaryReader(s);
  775. int x, y;
  776. for (x = 0; x < dimensionX; x++)
  777. {
  778. for (y = 0; y < dimensionY; y++)
  779. {
  780. tempMap[x, y] = (double) bs.ReadSingle();
  781. }
  782. }
  783. for (y = 0; y < h; y++)
  784. {
  785. for (x = 0; x < w; x++)
  786. {
  787. heightmap.Set(x, y, tempMap[x + sectionToLoadX, y + sectionToLoadY]);
  788. }
  789. }
  790. bs.Close();
  791. s.Close();
  792. tainted++;
  793. }
  794. finally
  795. {
  796. fileIOLock.ReleaseMutex();
  797. }
  798. }
  799. /// <summary>
  800. /// Loads a larger tiled image across a terrain
  801. /// </summary>
  802. /// <param name="filename">Filename to load from (any generic image format should work)</param>
  803. /// <param name="dimensionX">The dimensions of the image</param>
  804. /// <param name="dimensionY">The dimensions of the image</param>
  805. /// <param name="lowerboundX">Where sim coords begin for this patch</param>
  806. /// <param name="lowerboundY">Where sim coords begin for this patch</param>
  807. public void LoadFromFileIMG(string filename, int dimensionX, int dimensionY, int lowerboundX, int lowerboundY)
  808. {
  809. int sectionToLoadX = ((offsetX - lowerboundX)*w);
  810. int sectionToLoadY = ((offsetY - lowerboundY)*h);
  811. double[,] tempMap = new double[dimensionX,dimensionY];
  812. Bitmap lgrBmp = new Bitmap(filename);
  813. int x, y;
  814. for (x = 0; x < dimensionX; x++)
  815. {
  816. for (y = 0; y < dimensionY; y++)
  817. {
  818. tempMap[x, y] = (float) lgrBmp.GetPixel(x, y).GetBrightness();
  819. }
  820. }
  821. for (y = 0; y < h; y++)
  822. {
  823. for (x = 0; x < w; x++)
  824. {
  825. heightmap.Set(x, y, tempMap[x + sectionToLoadX, y + sectionToLoadY]);
  826. }
  827. }
  828. tainted++;
  829. }
  830. /// <summary>
  831. /// Loads a file formatted in the SL .RAW Format used on the main grid
  832. /// </summary>
  833. /// <remarks>This file format stinks and is best avoided.</remarks>
  834. /// <param name="filename">A path to the .RAW format</param>
  835. public void LoadFromFileSLRAW(string filename)
  836. {
  837. FileInfo file = new FileInfo(filename);
  838. FileStream s = file.Open(FileMode.Open, FileAccess.Read);
  839. BinaryReader bs = new BinaryReader(s);
  840. int x, y;
  841. for (y = 0; y < h; y++)
  842. {
  843. for (x = 0; x < w; x++)
  844. {
  845. heightmap.Set(x, y, (double) bs.ReadByte()*((double) bs.ReadByte()/127.0));
  846. bs.ReadBytes(11); // Advance the stream to next bytes.
  847. }
  848. }
  849. bs.Close();
  850. s.Close();
  851. tainted++;
  852. }
  853. /// <summary>
  854. /// Loads a section of a larger heightmap (RAW)
  855. /// </summary>
  856. /// <param name="filename">File to load</param>
  857. /// <param name="dimensionX">Size of the file</param>
  858. /// <param name="dimensionY">Size of the file</param>
  859. /// <param name="lowerboundX">Where do the region coords start for this terrain?</param>
  860. /// <param name="lowerboundY">Where do the region coords start for this terrain?</param>
  861. public void LoadFromFileSLRAW(string filename, int dimensionX, int dimensionY, int lowerboundX, int lowerboundY)
  862. {
  863. // TODO: Mutex fails to release readlock on folder! --> only one file can be loaded into sim
  864. //fileIOLock.WaitOne();
  865. //try
  866. //{
  867. int sectionToLoadX = ((offsetX - lowerboundX)*w);
  868. int sectionToLoadY = ((offsetY - lowerboundY)*h);
  869. double[,] tempMap = new double[dimensionX,dimensionY];
  870. FileInfo file = new FileInfo(filename);
  871. FileStream s = file.Open(FileMode.Open, FileAccess.Read);
  872. BinaryReader bs = new BinaryReader(s);
  873. int x, y;
  874. for (x = 0; x < dimensionX; x++)
  875. {
  876. for (y = 0; y < dimensionY; y++)
  877. {
  878. tempMap[x, y] = (double) bs.ReadByte()*((double) bs.ReadByte()/127.0);
  879. bs.ReadBytes(11); // Advance the stream to next bytes.
  880. }
  881. }
  882. for (y = 0; y < h; y++)
  883. {
  884. for (x = 0; x < w; x++)
  885. {
  886. heightmap.Set(x, y, tempMap[x + sectionToLoadX, y + sectionToLoadY]);
  887. }
  888. }
  889. bs.Close();
  890. s.Close();
  891. tainted++;
  892. //}
  893. //finally
  894. //{
  895. // fileIOLock.ReleaseMutex();
  896. //}
  897. }
  898. /// <summary>
  899. /// Writes the current terrain heightmap to disk, in the format of a 65536 entry double[] array.
  900. /// </summary>
  901. /// <param name="filename">The desired output filename</param>
  902. public void WriteToFileF64(string filename)
  903. {
  904. FileInfo file = new FileInfo(filename);
  905. FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write);
  906. BinaryWriter bs = new BinaryWriter(s);
  907. int x, y;
  908. for (y = 0; y < h; y++)
  909. {
  910. for (x = 0; x < w; x++)
  911. {
  912. bs.Write(heightmap.Get(x, y));
  913. }
  914. }
  915. bs.Close();
  916. s.Close();
  917. }
  918. /// <summary>
  919. /// Writes the current terrain heightmap to disk, in the format of a 65536 entry float[] array
  920. /// </summary>
  921. /// <param name="filename">The desired output filename</param>
  922. public void WriteToFileF32(string filename)
  923. {
  924. FileInfo file = new FileInfo(filename);
  925. FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write);
  926. BinaryWriter bs = new BinaryWriter(s);
  927. int x, y;
  928. for (y = 0; y < h; y++)
  929. {
  930. for (x = 0; x < w; x++)
  931. {
  932. bs.Write((float) heightmap.Get(x, y));
  933. }
  934. }
  935. bs.Close();
  936. s.Close();
  937. }
  938. /// <summary>
  939. /// A very fast LL-RAW file output mechanism - lower precision mechanism but wont take 5 minutes to run either.
  940. /// (is also editable in an image application)
  941. /// </summary>
  942. /// <param name="filename">Filename to write to</param>
  943. public void WriteToFileRAW(string filename)
  944. {
  945. FileInfo file = new FileInfo(filename);
  946. FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write);
  947. BinaryWriter binStream = new BinaryWriter(s);
  948. int x, y;
  949. // Used for the 'green' channel.
  950. byte avgMultiplier = (byte) heightmap.Avg();
  951. byte backupMultiplier = (byte) revertmap.Avg();
  952. // Limit the multiplier so it can represent points >64m.
  953. if (avgMultiplier > 196)
  954. avgMultiplier = 196;
  955. if (backupMultiplier > 196)
  956. backupMultiplier = 196;
  957. // Make sure it's at least one to prevent a div by zero
  958. if (avgMultiplier < 1)
  959. avgMultiplier = 1;
  960. if (backupMultiplier < 1)
  961. backupMultiplier = 1;
  962. for (y = 0; y < h; y++)
  963. {
  964. for (x = 0; x < h; x++)
  965. {
  966. byte red = (byte) (heightmap.Get(x, y)/((double) avgMultiplier/128.0));
  967. byte green = avgMultiplier;
  968. byte blue = (byte) watermap.Get(x, y);
  969. byte alpha1 = 0; // Land Parcels
  970. byte alpha2 = 0; // For Sale Land
  971. byte alpha3 = 0; // Public Edit Object
  972. byte alpha4 = 0; // Public Edit Land
  973. byte alpha5 = 255; // Safe Land
  974. byte alpha6 = 255; // Flying Allowed
  975. byte alpha7 = 255; // Create Landmark
  976. byte alpha8 = 255; // Outside Scripts
  977. byte alpha9 = (byte) (revertmap.Get(x, y)/((double) backupMultiplier/128.0));
  978. byte alpha10 = backupMultiplier;
  979. binStream.Write(red);
  980. binStream.Write(green);
  981. binStream.Write(blue);
  982. binStream.Write(alpha1);
  983. binStream.Write(alpha2);
  984. binStream.Write(alpha3);
  985. binStream.Write(alpha4);
  986. binStream.Write(alpha5);
  987. binStream.Write(alpha6);
  988. binStream.Write(alpha7);
  989. binStream.Write(alpha8);
  990. binStream.Write(alpha9);
  991. binStream.Write(alpha10);
  992. }
  993. }
  994. binStream.Close();
  995. s.Close();
  996. }
  997. /// <summary>
  998. /// Outputs to a LL compatible RAW in the most efficient manner possible
  999. /// </summary>
  1000. /// <remarks>Does not calculate the revert map</remarks>
  1001. /// <param name="filename">The filename to output to</param>
  1002. public void WriteToFileHiRAW(string filename)
  1003. {
  1004. FileInfo file = new FileInfo(filename);
  1005. FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write);
  1006. BinaryWriter binStream = new BinaryWriter(s);
  1007. // Generate a smegging big lookup table to speed the operation up (it needs it)
  1008. double[] lookupHeightTable = new double[65536];
  1009. int i, j, x, y;
  1010. for (i = 0; i < 256; i++)
  1011. {
  1012. for (j = 0; j < 256; j++)
  1013. {
  1014. lookupHeightTable[i + (j*256)] = ((double) i*((double) j/127.0));
  1015. }
  1016. }
  1017. // Output the calculated raw
  1018. for (y = 0; y < h; y++)
  1019. {
  1020. for (x = 0; x < w; x++)
  1021. {
  1022. double t = heightmap.Get(x, y);
  1023. double min = double.MaxValue;
  1024. int index = 0;
  1025. for (i = 0; i < 65536; i++)
  1026. {
  1027. if (Math.Abs(t - lookupHeightTable[i]) < min)
  1028. {
  1029. min = Math.Abs(t - lookupHeightTable[i]);
  1030. index = i;
  1031. }
  1032. }
  1033. byte red = (byte) (index & 0xFF);
  1034. byte green = (byte) ((index >> 8) & 0xFF);
  1035. byte blue = (byte) watermap.Get(x, y);
  1036. byte alpha1 = 0; // Land Parcels
  1037. byte alpha2 = 0; // For Sale Land
  1038. byte alpha3 = 0; // Public Edit Object
  1039. byte alpha4 = 0; // Public Edit Land
  1040. byte alpha5 = 255; // Safe Land
  1041. byte alpha6 = 255; // Flying Allowed
  1042. byte alpha7 = 255; // Create Landmark
  1043. byte alpha8 = 255; // Outside Scripts
  1044. byte alpha9 = red;
  1045. byte alpha10 = green;
  1046. binStream.Write(red);
  1047. binStream.Write(green);
  1048. binStream.Write(blue);
  1049. binStream.Write(alpha1);
  1050. binStream.Write(alpha2);
  1051. binStream.Write(alpha3);
  1052. binStream.Write(alpha4);
  1053. binStream.Write(alpha5);
  1054. binStream.Write(alpha6);
  1055. binStream.Write(alpha7);
  1056. binStream.Write(alpha8);
  1057. binStream.Write(alpha9);
  1058. binStream.Write(alpha10);
  1059. }
  1060. }
  1061. binStream.Close();
  1062. s.Close();
  1063. }
  1064. /// <summary>
  1065. /// Sets the random seed to be used by procedural functions which involve random numbers.
  1066. /// </summary>
  1067. /// <param name="val">The desired seed</param>
  1068. public void SetSeed(int val)
  1069. {
  1070. heightmap.seed = val;
  1071. }
  1072. /// <summary>
  1073. /// Sets a particular heightmap point to a specified value
  1074. /// </summary>
  1075. /// <param name="x">X Coordinate</param>
  1076. /// <param name="y">Y Coordinate</param>
  1077. /// <param name="val">Value</param>
  1078. public void Set(int x, int y, double val)
  1079. {
  1080. lock (heightmap)
  1081. {
  1082. heightmap.Set(x, y, val);
  1083. }
  1084. tainted++;
  1085. }
  1086. /// <summary>
  1087. /// Raises land in a sphere around the specified coordinates
  1088. /// </summary>
  1089. /// <param name="rx">Center of the sphere on the X axis</param>
  1090. /// <param name="ry">Center of the sphere on the Y axis</param>
  1091. /// <param name="size">The radius of the sphere</param>
  1092. /// <param name="amount">Scale the height of the sphere by this amount (recommended 0..2)</param>
  1093. public void RaiseTerrain(double rx, double ry, double size, double amount)
  1094. {
  1095. lock (heightmap)
  1096. {
  1097. heightmap.Raise(rx, ry, size, amount);
  1098. }
  1099. tainted++;
  1100. }
  1101. /// <summary>
  1102. /// Lowers the land in a sphere around the specified coordinates
  1103. /// </summary>
  1104. /// <param name="rx">The center of the sphere at the X axis</param>
  1105. /// <param name="ry">The center of the sphere at the Y axis</param>
  1106. /// <param name="size">The radius of the sphere in meters</param>
  1107. /// <param name="amount">Scale the height of the sphere by this amount (recommended 0..2)</param>
  1108. public void LowerTerrain(double rx, double ry, double size, double amount)
  1109. {
  1110. lock (heightmap)
  1111. {
  1112. heightmap.Lower(rx, ry, size, amount);
  1113. }
  1114. tainted++;
  1115. }
  1116. /// <summary>
  1117. /// Flattens the land under the brush of specified coordinates (spherical mask)
  1118. /// </summary>
  1119. /// <param name="rx">Center of sphere</param>
  1120. /// <param name="ry">Center of sphere</param>
  1121. /// <param name="size">Radius of the sphere</param>
  1122. /// <param name="amount">Thickness of the mask (0..2 recommended)</param>
  1123. public void FlattenTerrain(double rx, double ry, double size, double amount)
  1124. {
  1125. lock (heightmap)
  1126. {
  1127. heightmap.Flatten(rx, ry, size, amount);
  1128. }
  1129. tainted++;
  1130. }
  1131. /// <summary>
  1132. /// Creates noise within the specified bounds
  1133. /// </summary>
  1134. /// <param name="rx">Center of the bounding sphere</param>
  1135. /// <param name="ry">Center of the bounding sphere</param>
  1136. /// <param name="size">The radius of the sphere</param>
  1137. /// <param name="amount">Strength of the mask (0..2) recommended</param>
  1138. public void NoiseTerrain(double rx, double ry, double size, double amount)
  1139. {
  1140. lock (heightmap)
  1141. {
  1142. Channel smoothed = new Channel();
  1143. smoothed.Noise();
  1144. Channel mask = new Channel();
  1145. mask.Raise(rx, ry, size, amount);
  1146. heightmap.Blend(smoothed, mask);
  1147. }
  1148. tainted++;
  1149. }
  1150. /// <summary>
  1151. /// Reverts land within the specified bounds
  1152. /// </summary>
  1153. /// <param name="rx">Center of the bounding sphere</param>
  1154. /// <param name="ry">Center of the bounding sphere</param>
  1155. /// <param name="size">The radius of the sphere</param>
  1156. /// <param name="amount">Strength of the mask (0..2) recommended</param>
  1157. public void RevertTerrain(double rx, double ry, double size, double amount)
  1158. {
  1159. lock (heightmap)
  1160. {
  1161. Channel mask = new Channel();
  1162. mask.Raise(rx, ry, size, amount);
  1163. heightmap.Blend(revertmap, mask);
  1164. }
  1165. tainted++;
  1166. }
  1167. /// <summary>
  1168. /// Smooths land under the brush of specified coordinates (spherical mask)
  1169. /// </summary>
  1170. /// <param name="rx">Center of the sphere</param>
  1171. /// <param name="ry">Center of the sphere</param>
  1172. /// <param name="size">Radius of the sphere</param>
  1173. /// <param name="amount">Thickness of the mask (0..2 recommended)</param>
  1174. public void SmoothTerrain(double rx, double ry, double size, double amount)
  1175. {
  1176. lock (heightmap)
  1177. {
  1178. // perform essential computation as a channel method
  1179. heightmap.SmoothRegion(rx, ry, size, amount);
  1180. }
  1181. tainted++;
  1182. }
  1183. /// <summary>
  1184. /// Generates a simple set of hills in the shape of an island
  1185. /// </summary>
  1186. public void SetDefaultTerrain()
  1187. {
  1188. lock (heightmap)
  1189. {
  1190. heightmap.HillsSpheres(200, 20, 40, true, true, false);
  1191. heightmap.Normalise();
  1192. heightmap *= 60.0; // Raise to 60m
  1193. heightmap.Clip(0.0, 25.0);
  1194. heightmap.Pertubation(2.5);
  1195. heightmap.Smooth(35.0);
  1196. heightmap.Normalise(0.0, 21.0);
  1197. }
  1198. tainted++;
  1199. }
  1200. /// <summary>
  1201. /// Wrapper to heightmap.get()
  1202. /// </summary>
  1203. /// <param name="x">X coord</param>
  1204. /// <param name="y">Y coord</param>
  1205. /// <returns>Height at specified coordinates</returns>
  1206. public double GetHeight(int x, int y)
  1207. {
  1208. return heightmap.Get(x, y);
  1209. }
  1210. /// <summary>
  1211. /// Multiplies the heightfield by val
  1212. /// </summary>
  1213. /// <param name="meep">The heightfield</param>
  1214. /// <param name="val">The multiplier</param>
  1215. /// <returns></returns>
  1216. public static TerrainEngine operator *(TerrainEngine terrain, Double val)
  1217. {
  1218. terrain.heightmap *= val;
  1219. terrain.tainted++;
  1220. return terrain;
  1221. }
  1222. /// <summary>
  1223. /// Exports the current heightmap to a PNG file
  1224. /// </summary>
  1225. /// <param name="filename">The destination filename for the image</param>
  1226. /// <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>
  1227. public void WriteImage(string filename, string gradientmap)
  1228. {
  1229. try
  1230. {
  1231. Bitmap bmp = TerrainToBitmap(gradientmap);
  1232. bmp.Save(filename, ImageFormat.Png);
  1233. }
  1234. catch (Exception e) // LEGIT: Catching problems caused by OpenJPEG p/invoke
  1235. {
  1236. Console.WriteLine("Failed generating terrain map: " + e.ToString());
  1237. }
  1238. }
  1239. /// <summary>
  1240. /// Exports the current heightmap in Jpeg2000 format to a byte[]
  1241. /// </summary>
  1242. /// <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>
  1243. public byte[] WriteJpegImage(string gradientmap)
  1244. {
  1245. byte[] imageData = null;
  1246. try
  1247. {
  1248. Bitmap bmp = TerrainToBitmap(gradientmap);
  1249. imageData = OpenJPEG.EncodeFromImage(bmp, true);
  1250. }
  1251. catch (Exception e) // LEGIT: Catching problems caused by OpenJPEG p/invoke
  1252. {
  1253. Console.WriteLine("Failed generating terrain map: " + e.ToString());
  1254. }
  1255. return imageData;
  1256. }
  1257. private Bitmap TerrainToBitmap(string gradientmap)
  1258. {
  1259. Bitmap gradientmapLd = new Bitmap(gradientmap);
  1260. int pallete = gradientmapLd.Height;
  1261. Bitmap bmp = new Bitmap(heightmap.w, heightmap.h);
  1262. Color[] colours = new Color[pallete];
  1263. for (int i = 0; i < pallete; i++)
  1264. {
  1265. colours[i] = gradientmapLd.GetPixel(0, i);
  1266. }
  1267. Channel copy = heightmap.Copy();
  1268. for (int y = 0; y < copy.h; y++)
  1269. {
  1270. for (int x = 0; x < copy.w; x++)
  1271. {
  1272. // 512 is the largest possible height before colours clamp
  1273. int colorindex = (int) (Math.Max(Math.Min(1.0, copy.Get(x, y)/512.0), 0.0)*(pallete - 1));
  1274. bmp.SetPixel(x, copy.h - y - 1, colours[colorindex]);
  1275. }
  1276. }
  1277. return bmp;
  1278. }
  1279. }
  1280. }