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