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