aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authorTeravus Ovares2008-08-22 22:04:43 +0000
committerTeravus Ovares2008-08-22 22:04:43 +0000
commit50bf3618a32bf72bcbd88e56610cf955737ff388 (patch)
treef12cc710f25878c47cc23de7c426fc9cebffea4d /OpenSim
parent* Allow an exception generated in ProcessOutPacket to travel up the stack (diff)
downloadopensim-SC-50bf3618a32bf72bcbd88e56610cf955737ff388.zip
opensim-SC-50bf3618a32bf72bcbd88e56610cf955737ff388.tar.gz
opensim-SC-50bf3618a32bf72bcbd88e56610cf955737ff388.tar.bz2
opensim-SC-50bf3618a32bf72bcbd88e56610cf955737ff388.tar.xz
* Homer's amazing terrain MapTileRenderer. Thanks Homer!
* By default, texture rendering is on. This may be affected by using secure assets since your region hasn't registered with the gridserver before it asks for texture assets. It might also be affected by a slow asset server, so consider this release experimental. * Defined interface IMapTileTerrainRenderer. * Extracted "old" shaded maptile terrain rendering into ShadedMapTileRenderer; streamlined it a bit and added "highlight" rendering to its "shadow" rendering. * Added "new" terrain-texture based maptile terrain rendering (TexturedMapTileRenderer); made TerrainUtil.InterpolatedNoise public * Adapted MapImageModule to allow switching between those two by configuration * Added configuration option to OpenSim.ini.example
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Environment/Modules/World/Terrain/TerrainUtil.cs4
-rw-r--r--OpenSim/Region/Environment/Modules/World/WorldMap/IMapTileTerrainRenderer.cs39
-rw-r--r--OpenSim/Region/Environment/Modules/World/WorldMap/MapImageModule.cs203
-rw-r--r--OpenSim/Region/Environment/Modules/World/WorldMap/ShadedMapTileRenderer.cs254
-rw-r--r--OpenSim/Region/Environment/Modules/World/WorldMap/TexturedMapTileRenderer.cs386
5 files changed, 704 insertions, 182 deletions
diff --git a/OpenSim/Region/Environment/Modules/World/Terrain/TerrainUtil.cs b/OpenSim/Region/Environment/Modules/World/Terrain/TerrainUtil.cs
index 4cb828e..def28eb 100644
--- a/OpenSim/Region/Environment/Modules/World/Terrain/TerrainUtil.cs
+++ b/OpenSim/Region/Environment/Modules/World/Terrain/TerrainUtil.cs
@@ -95,7 +95,7 @@ namespace OpenSim.Region.Environment.Modules.World.Terrain
95 return (x * (1.0 - z)) + (y * z); 95 return (x * (1.0 - z)) + (y * z);
96 } 96 }
97 97
98 private static double InterpolatedNoise(double x, double y) 98 public static double InterpolatedNoise(double x, double y)
99 { 99 {
100 int integer_X = (int) (x); 100 int integer_X = (int) (x);
101 double fractional_X = x - integer_X; 101 double fractional_X = x - integer_X;
@@ -128,4 +128,4 @@ namespace OpenSim.Region.Environment.Modules.World.Terrain
128 return total; 128 return total;
129 } 129 }
130 } 130 }
131} \ No newline at end of file 131}
diff --git a/OpenSim/Region/Environment/Modules/World/WorldMap/IMapTileTerrainRenderer.cs b/OpenSim/Region/Environment/Modules/World/WorldMap/IMapTileTerrainRenderer.cs
new file mode 100644
index 0000000..bebcc52
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/World/WorldMap/IMapTileTerrainRenderer.cs
@@ -0,0 +1,39 @@
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
28using System.Drawing;
29using OpenSim.Region.Environment.Scenes;
30using Nini.Config;
31
32namespace OpenSim.Region.Environment
33{
34 public interface IMapTileTerrainRenderer
35 {
36 void Initialise(Scene scene, IConfigSource config);
37 void TerrainToBitmap(Bitmap mapbmp);
38 }
39}
diff --git a/OpenSim/Region/Environment/Modules/World/WorldMap/MapImageModule.cs b/OpenSim/Region/Environment/Modules/World/WorldMap/MapImageModule.cs
index 38c52f0..2ae4174 100644
--- a/OpenSim/Region/Environment/Modules/World/WorldMap/MapImageModule.cs
+++ b/OpenSim/Region/Environment/Modules/World/WorldMap/MapImageModule.cs
@@ -69,29 +69,47 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
69 69
70 private Scene m_scene; 70 private Scene m_scene;
71 private IConfigSource m_config; 71 private IConfigSource m_config;
72 private IMapTileTerrainRenderer terrainRenderer;
72 73
73 #region IMapImageGenerator Members 74 #region IMapImageGenerator Members
74 75
75 public byte[] WriteJpeg2000Image(string gradientmap) 76 public byte[] WriteJpeg2000Image(string gradientmap)
76 { 77 {
77 byte[] imageData = null; 78 byte[] imageData = null;
78 Bitmap mapbmp = new Bitmap(256, 256);
79
80 //Bitmap bmp = TerrainToBitmap(gradientmap);
81 mapbmp = TerrainToBitmap2(m_scene,mapbmp);
82 79
83 bool drawPrimVolume = true; 80 bool drawPrimVolume = true;
81 bool textureTerrain = true;
84 82
85 try 83 try
86 { 84 {
87 IConfig startupConfig = m_config.Configs["Startup"]; 85 IConfig startupConfig = m_config.Configs["Startup"];
88 drawPrimVolume = startupConfig.GetBoolean("DrawPrimOnMapTile", true); 86 drawPrimVolume = startupConfig.GetBoolean("DrawPrimOnMapTile", drawPrimVolume);
87 textureTerrain = startupConfig.GetBoolean("TextureOnMapTile", textureTerrain);
89 } 88 }
90 catch (Exception) 89 catch (Exception)
91 { 90 {
92 m_log.Warn("Failed to load StartupConfig"); 91 m_log.Warn("Failed to load StartupConfig");
93 } 92 }
94 93
94 if (textureTerrain)
95 {
96 terrainRenderer = new TexturedMapTileRenderer();
97 }
98 else
99 {
100 terrainRenderer = new ShadedMapTileRenderer();
101 }
102 terrainRenderer.Initialise(m_scene, m_config);
103
104 Bitmap mapbmp = new Bitmap(256, 256);
105 //long t = System.Environment.TickCount;
106 //for(int i = 0; i < 10; ++i) {
107 terrainRenderer.TerrainToBitmap(mapbmp);
108 //}
109 //t = System.Environment.TickCount - t;
110 //m_log.InfoFormat("[MAPTILE] generation of 10 maptiles needed {0} ms", t);
111
112
95 if (drawPrimVolume) 113 if (drawPrimVolume)
96 { 114 {
97 DrawObjectVolume(m_scene, mapbmp); 115 DrawObjectVolume(m_scene, mapbmp);
@@ -181,181 +199,6 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
181// } 199// }
182// } 200// }
183 201
184 private Bitmap TerrainToBitmap2(Scene whichScene, Bitmap mapbmp)
185 {
186 int tc = System.Environment.TickCount;
187 m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain");
188
189 double[,] hm = whichScene.Heightmap.GetDoubles();
190 bool ShadowDebugContinue = true;
191
192 bool terraincorruptedwarningsaid = false;
193
194 float low = 255;
195 float high = 0;
196 for (int x = 0; x < 256; x++)
197 {
198 for (int y = 0; y < 256; y++)
199 {
200 float hmval = (float)hm[x, y];
201 if (hmval < low)
202 low = hmval;
203 if (hmval > high)
204 high = hmval;
205 }
206 }
207
208 float mid = (high + low) * 0.5f;
209
210 // temporary initializer
211 float hfvalue = (float)whichScene.RegionInfo.RegionSettings.WaterHeight;
212 float hfvaluecompare = hfvalue;
213 float hfdiff = hfvalue;
214 int hfdiffi = 0;
215
216
217 for (int x = 0; x < 256; x++)
218 {
219 //int tc = System.Environment.TickCount;
220 for (int y = 0; y < 256; y++)
221 {
222 float heightvalue = (float)hm[x, y];
223
224 if (heightvalue > (float)whichScene.RegionInfo.RegionSettings.WaterHeight)
225 {
226 // scale height value
227 heightvalue = low + mid * (heightvalue - low) / mid;
228
229 if (heightvalue > 255)
230 heightvalue = 255;
231
232 if (heightvalue < 0)
233 heightvalue = 0;
234
235 if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
236 heightvalue = 0;
237
238 try
239 {
240 Color green = Color.FromArgb((int)heightvalue, 100, (int)heightvalue);
241
242 // Y flip the cordinates
243 mapbmp.SetPixel(x, (256 - y) - 1, green);
244
245 //X
246 // .
247 //
248 // Shade the terrain for shadows
249 if ((x - 1 > 0) && (y - 1 > 0))
250 {
251 hfvalue = (float)hm[x, y];
252 hfvaluecompare = (float)hm[x - 1, y - 1];
253
254 if (Single.IsInfinity(hfvalue) || Single.IsNaN(hfvalue))
255 hfvalue = 0;
256
257 if (Single.IsInfinity(hfvaluecompare) || Single.IsNaN(hfvaluecompare))
258 hfvaluecompare = 0;
259
260 hfdiff = hfvaluecompare - hfvalue;
261
262 if (hfdiff > 0.3f)
263 {
264 }
265 else if (hfdiff < -0.3f)
266 {
267 // We have to desaturate and blacken the land at the same time
268 // we use floats, colors use bytes, so shrink are space down to
269 // 0-255
270
271
272 try
273 {
274 hfdiffi = Math.Abs((int)((hfdiff * 4) + (hfdiff * 0.5))) + 1;
275 if (hfdiff % 1 != 0)
276 {
277 hfdiffi = hfdiffi + Math.Abs((int)(((hfdiff % 1) * 0.5f) * 10f) - 1);
278 }
279 }
280 catch (System.OverflowException)
281 {
282 m_log.Debug("[MAPTILE]: Shadow failed at value: " + hfdiff.ToString());
283 ShadowDebugContinue = false;
284 }
285
286 if (ShadowDebugContinue)
287 {
288 if ((256 - y) - 1 > 0)
289 {
290 Color Shade = mapbmp.GetPixel(x - 1, (256 - y) - 1);
291
292 int r = Shade.R;
293
294 int g = Shade.G;
295 int b = Shade.B;
296 Shade = Color.FromArgb((r - hfdiffi > 0) ? r - hfdiffi : 0, (g - hfdiffi > 0) ? g - hfdiffi : 0, (b - hfdiffi > 0) ? b - hfdiffi : 0);
297 mapbmp.SetPixel(x - 1, (256 - y) - 1, Shade);
298 }
299 }
300 }
301 }
302 }
303 catch (System.ArgumentException)
304 {
305 if (!terraincorruptedwarningsaid)
306 {
307 m_log.WarnFormat("[MAPIMAGE]: Your terrain is corrupted in region {0}, it might take a few minutes to generate the map image depending on the corruption level", whichScene.RegionInfo.RegionName);
308 terraincorruptedwarningsaid = true;
309 }
310 Color black = Color.Black;
311 mapbmp.SetPixel(x, (256 - y) - 1, black);
312 }
313 }
314 else
315 {
316 // We're under the water level with the terrain, so paint water instead of land
317
318 // Y flip the cordinates
319 heightvalue = (float)whichScene.RegionInfo.RegionSettings.WaterHeight - heightvalue;
320 if (heightvalue > 19)
321 heightvalue = 19;
322 if (heightvalue < 0)
323 heightvalue = 0;
324
325 heightvalue = 100 - (heightvalue * 100) / 19;
326
327 if (heightvalue > 255)
328 heightvalue = 255;
329
330 if (heightvalue < 0)
331 heightvalue = 0;
332
333 if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
334 heightvalue = 0;
335
336 try
337 {
338 Color water = Color.FromArgb((int)heightvalue, (int)heightvalue, 255);
339 mapbmp.SetPixel(x, (256 - y) - 1, water);
340 }
341 catch (System.ArgumentException)
342 {
343 if (!terraincorruptedwarningsaid)
344 {
345 m_log.WarnFormat("[MAPIMAGE]: Your terrain is corrupted in region {0}, it might take a few minutes to generate the map image depending on the corruption level", whichScene.RegionInfo.RegionName);
346 terraincorruptedwarningsaid = true;
347 }
348 Color black = Color.Black;
349 mapbmp.SetPixel(x, (256 - y) - 1, black);
350 }
351 }
352 }
353 }
354 m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (System.Environment.TickCount - tc) + " ms");
355
356 return mapbmp;
357 }
358
359 private Bitmap DrawObjectVolume(Scene whichScene, Bitmap mapbmp) 202 private Bitmap DrawObjectVolume(Scene whichScene, Bitmap mapbmp)
360 { 203 {
361 int tc = 0; 204 int tc = 0;
diff --git a/OpenSim/Region/Environment/Modules/World/WorldMap/ShadedMapTileRenderer.cs b/OpenSim/Region/Environment/Modules/World/WorldMap/ShadedMapTileRenderer.cs
new file mode 100644
index 0000000..253a7f5
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/World/WorldMap/ShadedMapTileRenderer.cs
@@ -0,0 +1,254 @@
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
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Drawing;
32using System.Drawing.Drawing2D;
33using System.Drawing.Imaging;
34using System.Reflection;
35using Axiom.Math;
36using Nini.Config;
37using log4net;
38using OpenJPEGNet;
39using OpenSim.Region.Environment.Interfaces;
40using OpenSim.Region.Environment.Scenes;
41using libsecondlife;
42
43namespace OpenSim.Region.Environment.Modules.World.WorldMap
44{
45 public class ShadedMapTileRenderer : IMapTileTerrainRenderer
46 {
47 private static readonly ILog m_log =
48 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49
50 private Scene m_scene;
51 //private IConfigSource m_config; // not used currently
52
53 public void Initialise(Scene scene, IConfigSource config)
54 {
55 m_scene = scene;
56 // m_config = config; // not used currently
57 }
58
59 public void TerrainToBitmap(Bitmap mapbmp)
60 {
61 int tc = System.Environment.TickCount;
62 m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain");
63
64 double[,] hm = m_scene.Heightmap.GetDoubles();
65 bool ShadowDebugContinue = true;
66
67 bool terraincorruptedwarningsaid = false;
68
69 float low = 255;
70 float high = 0;
71 for (int x = 0; x < 256; x++)
72 {
73 for (int y = 0; y < 256; y++)
74 {
75 float hmval = (float)hm[x, y];
76 if (hmval < low)
77 low = hmval;
78 if (hmval > high)
79 high = hmval;
80 }
81 }
82
83 float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
84
85 for (int x = 0; x < 256; x++)
86 {
87 for (int y = 0; y < 256; y++)
88 {
89 // Y flip the cordinates for the bitmap: hf origin is lower left, bm origin is upper left
90 int yr = 255 - y;
91
92 float heightvalue = (float)hm[x, y];
93
94 if (heightvalue > waterHeight)
95 {
96 // scale height value
97 // No, that doesn't scale it:
98 // heightvalue = low + mid * (heightvalue - low) / mid; => low + (heightvalue - low) * mid / mid = low + (heightvalue - low) * 1 = low + heightvalue - low = heightvalue
99
100
101
102 if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
103 heightvalue = 0;
104 else if (heightvalue > 255f)
105 heightvalue = 255f;
106 else if (heightvalue < 0f)
107 heightvalue = 0f;
108
109 Color color = Color.FromArgb((int)heightvalue, 100, (int)heightvalue);
110
111 mapbmp.SetPixel(x, yr, color);
112
113 try
114 {
115 //X
116 // .
117 //
118 // Shade the terrain for shadows
119 if (x < 255 && yr < 255)
120 {
121 float hfvalue = (float)hm[x, y];
122 float hfvaluecompare = 0f;
123
124 if ((x + 1 < 256) && (y + 1 < 256))
125 {
126 hfvaluecompare = (float)hm[x + 1, y + 1]; // light from north-east => look at land height there
127 }
128 if (Single.IsInfinity(hfvalue) || Single.IsNaN(hfvalue))
129 hfvalue = 0f;
130
131 if (Single.IsInfinity(hfvaluecompare) || Single.IsNaN(hfvaluecompare))
132 hfvaluecompare = 0f;
133
134 float hfdiff = hfvalue - hfvaluecompare; // => positive if NE is lower, negative if here is lower
135
136 int hfdiffi = 0;
137 int hfdiffihighlight = 0;
138 float highlightfactor = 0.18f;
139
140 try
141 {
142 // hfdiffi = Math.Abs((int)((hfdiff * 4) + (hfdiff * 0.5))) + 1;
143 hfdiffi = Math.Abs((int)(hfdiff * 4.5f)) + 1;
144 if (hfdiff % 1f != 0)
145 {
146 // hfdiffi = hfdiffi + Math.Abs((int)(((hfdiff % 1) * 0.5f) * 10f) - 1);
147 hfdiffi = hfdiffi + Math.Abs((int)((hfdiff % 1f) * 5f) - 1);
148 }
149
150 hfdiffihighlight = Math.Abs((int)((hfdiff * highlightfactor) * 4.5f)) + 1;
151 if (hfdiff % 1f != 0)
152 {
153 // hfdiffi = hfdiffi + Math.Abs((int)(((hfdiff % 1) * 0.5f) * 10f) - 1);
154 hfdiffihighlight = hfdiffihighlight + Math.Abs((int)(((hfdiff * highlightfactor) % 1f) * 5f) - 1);
155 }
156 }
157 catch (System.OverflowException)
158 {
159 m_log.Debug("[MAPTILE]: Shadow failed at value: " + hfdiff.ToString());
160 ShadowDebugContinue = false;
161 }
162
163 if (hfdiff > 0.3f)
164 {
165 // NE is lower than here
166 // We have to desaturate and lighten the land at the same time
167 // we use floats, colors use bytes, so shrink are space down to
168 // 0-255
169
170 if (ShadowDebugContinue)
171 {
172 int r = color.R;
173 int g = color.G;
174 int b = color.B;
175 color = Color.FromArgb((r + hfdiffihighlight < 255) ? r + hfdiffihighlight : 255,
176 (g + hfdiffihighlight < 255) ? g + hfdiffihighlight : 255,
177 (b + hfdiffihighlight < 255) ? b + hfdiffihighlight : 255);
178 }
179 }
180 else if (hfdiff < -0.3f)
181 {
182 // here is lower than NE:
183 // We have to desaturate and blacken the land at the same time
184 // we use floats, colors use bytes, so shrink are space down to
185 // 0-255
186
187 if (ShadowDebugContinue)
188 {
189 if ((x - 1 > 0) && (yr + 1 < 256))
190 {
191 color = mapbmp.GetPixel(x - 1, yr + 1);
192 int r = color.R;
193 int g = color.G;
194 int b = color.B;
195 color = Color.FromArgb((r - hfdiffi > 0) ? r - hfdiffi : 0,
196 (g - hfdiffi > 0) ? g - hfdiffi : 0,
197 (b - hfdiffi > 0) ? b - hfdiffi : 0);
198
199 mapbmp.SetPixel(x-1, yr+1, color);
200 }
201
202 }
203 }
204 }
205 }
206 catch (System.ArgumentException)
207 {
208 if (!terraincorruptedwarningsaid)
209 {
210 m_log.WarnFormat("[MAPIMAGE]: Your terrain is corrupted in region {0}, it might take a few minutes to generate the map image depending on the corruption level", m_scene.RegionInfo.RegionName);
211 terraincorruptedwarningsaid = true;
212 }
213 color = Color.Black;
214 mapbmp.SetPixel(x, yr, color);
215 }
216
217 }
218 else
219 {
220 // We're under the water level with the terrain, so paint water instead of land
221
222 // Y flip the cordinates
223 heightvalue = waterHeight - heightvalue;
224 if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
225 heightvalue = 0f;
226 else if (heightvalue > 19f)
227 heightvalue = 19f;
228 else if (heightvalue < 0f)
229 heightvalue = 0f;
230
231 heightvalue = 100f - (heightvalue * 100f) / 19f;
232
233 try
234 {
235 Color water = Color.FromArgb((int)heightvalue, (int)heightvalue, 255);
236 mapbmp.SetPixel(x, yr, water);
237 }
238 catch (System.ArgumentException)
239 {
240 if (!terraincorruptedwarningsaid)
241 {
242 m_log.WarnFormat("[MAPIMAGE]: Your terrain is corrupted in region {0}, it might take a few minutes to generate the map image depending on the corruption level", m_scene.RegionInfo.RegionName);
243 terraincorruptedwarningsaid = true;
244 }
245 Color black = Color.Black;
246 mapbmp.SetPixel(x, (256 - y) - 1, black);
247 }
248 }
249 }
250 }
251 m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (System.Environment.TickCount - tc) + " ms");
252 }
253 }
254}
diff --git a/OpenSim/Region/Environment/Modules/World/WorldMap/TexturedMapTileRenderer.cs b/OpenSim/Region/Environment/Modules/World/WorldMap/TexturedMapTileRenderer.cs
new file mode 100644
index 0000000..957a1df
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/World/WorldMap/TexturedMapTileRenderer.cs
@@ -0,0 +1,386 @@
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
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Drawing;
32using System.Drawing.Drawing2D;
33using System.Drawing.Imaging;
34using System.Reflection;
35using Axiom.Math;
36using Nini.Config;
37using log4net;
38using OpenJPEGNet;
39using OpenSim.Framework;
40using OpenSim.Region.Environment.Interfaces;
41using OpenSim.Region.Environment.Scenes;
42using OpenSim.Region.Environment.Modules.World.Terrain;
43using libsecondlife;
44
45namespace OpenSim.Region.Environment.Modules.World.WorldMap
46{
47 // Hue, Saturation, Value; used for color-interpolation
48 struct HSV {
49 public float h;
50 public float s;
51 public float v;
52
53 public HSV(float h, float s, float v)
54 {
55 this.h = h;
56 this.s = s;
57 this.v = v;
58 }
59
60 // (for info about algorithm, see http://en.wikipedia.org/wiki/HSL_and_HSV)
61 public HSV(Color c)
62 {
63 float r = c.R / 255f;
64 float g = c.G / 255f;
65 float b = c.B / 255f;
66 float max = Math.Max(Math.Max(r, g), b);
67 float min = Math.Min(Math.Min(r, g), b);
68 float diff = max - min;
69
70 if (max == min) h = 0f;
71 else if (max == r) h = (g - b) / diff * 60f;
72 else if (max == g) h = (b - r) / diff * 60f + 120f;
73 else h = (r - g) / diff * 60f + 240f;
74 if (h < 0f) h += 360f;
75
76 if (max == 0f) s = 0f;
77 else s = diff / max;
78
79 v = max;
80 }
81
82 // (for info about algorithm, see http://en.wikipedia.org/wiki/HSL_and_HSV)
83 public Color toColor()
84 {
85 if(s < 0f) Console.WriteLine("S < 0: " + s);
86 else if(s > 1f) Console.WriteLine("S > 1: " + s);
87 if(v < 0f) Console.WriteLine("V < 0: " + v);
88 else if(v > 1f) Console.WriteLine("V > 1: " + v);
89
90 float f = h / 60f;
91 int sector = (int)f % 6;
92 f = f - (int)f;
93 int pi = (int)(v * (1f - s) * 255f);
94 int qi = (int)(v * (1f - s * f) * 255f);
95 int ti = (int)(v * (1f - (1f - f) * s) * 255f);
96 int vi = (int)(v * 255f);
97
98 switch(sector)
99 {
100 case 0:
101 return Color.FromArgb(vi, ti, pi);
102 case 1:
103 return Color.FromArgb(qi, vi, pi);
104 case 2:
105 return Color.FromArgb(pi, vi, ti);
106 case 3:
107 return Color.FromArgb(pi, qi, vi);
108 case 4:
109 return Color.FromArgb(ti, pi, vi);
110 default:
111 return Color.FromArgb(vi, pi, qi);
112 }
113 }
114 }
115
116 public class TexturedMapTileRenderer : IMapTileTerrainRenderer
117 {
118 #region Constants
119
120 private static readonly ILog m_log =
121 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
122
123 // some hardcoded terrain UUIDs that work with SL 1.20 (the four default textures and "Blank").
124 // The color-values were choosen because they "look right" (at least to me) ;-)
125 private static readonly LLUUID defaultTerrainTexture1 = new LLUUID("0bc58228-74a0-7e83-89bc-5c23464bcec5");
126 private static readonly Color defaultColor1 = Color.FromArgb(165, 137, 118);
127 private static readonly LLUUID defaultTerrainTexture2 = new LLUUID("63338ede-0037-c4fd-855b-015d77112fc8");
128 private static readonly Color defaultColor2 = Color.FromArgb(69, 89, 49);
129 private static readonly LLUUID defaultTerrainTexture3 = new LLUUID("303cd381-8560-7579-23f1-f0a880799740");
130 private static readonly Color defaultColor3 = Color.FromArgb(162, 154, 141);
131 private static readonly LLUUID defaultTerrainTexture4 = new LLUUID("53a2f406-4895-1d13-d541-d2e3b86bc19c");
132 private static readonly Color defaultColor4 = Color.FromArgb(200, 200, 200);
133 private static readonly LLUUID blankTerrainTexture = new LLUUID("5748decc-f629-461c-9a36-a35a221fe21f");
134
135 #endregion
136
137
138 private Scene m_scene;
139 // private IConfigSource m_config; // not used currently
140
141 // mapping from texture UUIDs to averaged color. This will contain 5-9 values, in general; new values are only
142 // added when the terrain textures are changed in the estate dialog and a new map is generated (and will stay in
143 // that map until the region-server restarts. This could be considered a memory-leak, but it's a *very* small one.
144 // TODO does it make sense to use a "real" cache and regenerate missing entries on fetch?
145 private Dictionary<LLUUID, Color> m_mapping;
146
147
148 public void Initialise(Scene scene, IConfigSource source)
149 {
150 m_scene = scene;
151 // m_config = source; // not used currently
152 m_mapping = new Dictionary<LLUUID,Color>();
153 m_mapping.Add(defaultTerrainTexture1, defaultColor1);
154 m_mapping.Add(defaultTerrainTexture2, defaultColor2);
155 m_mapping.Add(defaultTerrainTexture3, defaultColor3);
156 m_mapping.Add(defaultTerrainTexture4, defaultColor4);
157 m_mapping.Add(blankTerrainTexture, Color.White);
158 }
159
160 #region Helpers
161 // This fetches the texture from the asset server synchroneously. That should be ok, as we
162 // call map-creation only in those places:
163 // - on start: We can wait here until the asset server returns the texture
164 // TODO (- on "map" command: We are in the command-line thread, we will wait for completion anyway)
165 // TODO (- on "automatic" update after some change: We are called from the mapUpdateTimer here and
166 // will wait anyway)
167 private Bitmap fetchTexture(LLUUID id)
168 {
169 AssetBase asset = m_scene.AssetCache.GetAsset(id, true);
170 m_log.DebugFormat("Fetched texture {0}, found: {1}", id, asset != null);
171 if(asset == null) return null;
172 return new Bitmap(OpenJPEG.DecodeToImage(asset.Data));
173 }
174
175 // Compute the average color of a texture.
176 private Color computeAverageColor(Bitmap bmp)
177 {
178 // we have 256 x 256 pixel, each with 256 possible color-values per
179 // color-channel, so 2^24 is the maximum value we can get, adding everything.
180 // int is be big enough for that.
181 int r = 0, g = 0, b = 0;
182 for(int y = 0; y < bmp.Height; ++y)
183 {
184 for(int x = 0; x < bmp.Width; ++x)
185 {
186 Color c = bmp.GetPixel(x, y);
187 r += (int)c.R & 0xff;
188 g += (int)c.G & 0xff;
189 b += (int)c.B & 0xff;
190 }
191 }
192
193 int pixels = bmp.Width * bmp.Height;
194 return Color.FromArgb(r / pixels, g / pixels, b / pixels);
195 }
196
197 // return either the average color of the texture, or the defaultColor if the texturID is invalid
198 // or the texture couldn't be found
199 private Color computeAverageColor(LLUUID textureID, Color defaultColor) {
200 if (textureID == LLUUID.Zero) return defaultColor; // not set
201 if (m_mapping.ContainsKey(textureID)) return m_mapping[textureID]; // one of the predefined textures
202
203 Bitmap bmp = fetchTexture(textureID);
204 Color color = bmp == null ? defaultColor : computeAverageColor(bmp);
205 // store it for future reference
206 m_mapping[textureID] = color;
207
208 return color;
209 }
210
211 // S-curve: f(x) = 3x² - 2x³:
212 // f(0) = 0, f(0.5) = 0.5, f(1) = 1,
213 // f'(x) = 0 at x = 0 and x = 1; f'(0.5) = 1.5,
214 // f''(0.5) = 0, f''(x) != 0 for x != 0.5
215 private float S(float v) {
216 return (v * v * (3f - 2f * v));
217 }
218
219 // interpolate two colors in HSV space and return the resulting color
220 private HSV interpolateHSV(ref HSV c1, ref HSV c2, float ratio) {
221 if(ratio <= 0f) return c1;
222 if(ratio >= 1f) return c2;
223
224 // make sure we are on the same side on the hue-circle for interpolation
225 // We change the hue of the parameters here, but we don't change the color
226 // represented by that value
227 if(c1.h - c2.h > 180f) c1.h -= 360f;
228 else if(c2.h - c1.h > 180f) c1.h += 360f;
229
230 return new HSV(c1.h * (1f - ratio) + c2.h * ratio,
231 c1.s * (1f - ratio) + c2.s * ratio,
232 c1.v * (1f - ratio) + c2.v * ratio);
233 }
234
235 // the heigthfield might have some jumps in values. Rendered land is smooth, though,
236 // as a slope is rendered at that place. So average 4 neighbour values to emulate that.
237 private float getHeight(double[,] hm, int x, int y) {
238 if (x < 255 && y < 255)
239 return (float)(hm[x, y] * .444 + (hm[x + 1, y] + hm[x, y + 1]) * .222 + hm[x + 1, y +1] * .112);
240 else
241 return (float)hm[x, y];
242 }
243 #endregion
244
245 public void TerrainToBitmap(Bitmap mapbmp)
246 {
247 int tc = System.Environment.TickCount;
248 m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain");
249
250 // These textures should be in the AssetCache anyway, as every client conneting to this
251 // region needs them. Except on start, when the map is recreated (before anyone connected),
252 // and on change of the estate settings (textures and terrain values), when the map should
253 // be recreated.
254 RegionSettings settings = m_scene.RegionInfo.RegionSettings;
255
256 // the four terrain colors as HSVs for interpolation
257 HSV hsv1 = new HSV(computeAverageColor(settings.TerrainTexture1, defaultColor1));
258 HSV hsv2 = new HSV(computeAverageColor(settings.TerrainTexture2, defaultColor2));
259 HSV hsv3 = new HSV(computeAverageColor(settings.TerrainTexture3, defaultColor3));
260 HSV hsv4 = new HSV(computeAverageColor(settings.TerrainTexture4, defaultColor4));
261
262 float levelNElow = (float)settings.Elevation1NE;
263 float levelNEhigh = (float)settings.Elevation2NE;
264
265 float levelNWlow = (float)settings.Elevation1NW;
266 float levelNWhigh = (float)settings.Elevation2NW;
267
268 float levelSElow = (float)settings.Elevation1SE;
269 float levelSEhigh = (float)settings.Elevation2SE;
270
271 float levelSWlow = (float)settings.Elevation1SW;
272 float levelSWhigh = (float)settings.Elevation2SW;
273
274 float waterHeight = (float)settings.WaterHeight;
275
276 double[,] hm = m_scene.Heightmap.GetDoubles();
277
278 for (int x = 0; x < 256; x++)
279 {
280 float columnRatio = x / 255f; // 0 - 1, for interpolation
281 for (int y = 0; y < 256; y++)
282 {
283 float rowRatio = y / 255f; // 0 - 1, for interpolation
284
285 // Y flip the cordinates for the bitmap: hf origin is lower left, bm origin is upper left
286 int yr = 255 - y;
287
288 float heightvalue = getHeight(hm, x, y);
289 if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
290 heightvalue = 0;
291
292 if (heightvalue > waterHeight)
293 {
294 // add a bit noise for breaking up those flat colors:
295 // - a large-scale noise, for the "patches" (using an doubled s-curve for sharper contrast)
296 // - a small-scale noise, for bringing in some small scale variation
297 //float bigNoise = (float)TerrainUtil.InterpolatedNoise(x / 8.0, y / 8.0) * .5f + .5f; // map to 0.0 - 1.0
298 //float smallNoise = (float)TerrainUtil.InterpolatedNoise(x + 33, y + 43) * .5f + .5f;
299 //float hmod = heightvalue + smallNoise * 3f + S(S(bigNoise)) * 10f;
300 float hmod =
301 heightvalue +
302 (float)TerrainUtil.InterpolatedNoise(x + 33, y + 43) * 1.5f + 1.5f + // 0 - 3
303 S(S((float)TerrainUtil.InterpolatedNoise(x / 8.0, y / 8.0) * .5f + .5f)) * 10f; // 0 - 10
304
305 // find the low/high values for this point (interpolated bilinearily)
306 // (and remember, x=0,y=0 is SW)
307 float low = levelSWlow * (1f - rowRatio) * (1f - columnRatio) +
308 levelSElow * (1f - rowRatio) * columnRatio +
309 levelNWlow * rowRatio * (1f - columnRatio) +
310 levelNElow * rowRatio * columnRatio;
311 float high = levelSWhigh * (1f - rowRatio) * (1f - columnRatio) +
312 levelSEhigh * (1f - rowRatio) * columnRatio +
313 levelNWhigh * rowRatio * (1f - columnRatio) +
314 levelNEhigh * rowRatio * columnRatio;
315 if (high < low)
316 {
317 // someone tried to fool us. High value should be higher than low every time
318 float tmp = high;
319 high = low;
320 low = tmp;
321 }
322
323 HSV hsv;
324 if(hmod <= low) hsv = hsv1; // too low
325 else if(hmod >= high) hsv = hsv4; // too high
326 else
327 {
328 // HSV-interpolate along the colors
329 // first, rescale h to 0.0 - 1.0
330 hmod = (hmod - low) / (high - low);
331 // now we have to split: 0.00 => color1, 0.33 => color2, 0.67 => color3, 1.00 => color4
332 if(hmod < 1f/3f) hsv = interpolateHSV(ref hsv1, ref hsv2, hmod * 3f);
333 else if(hmod < 2f/3f) hsv = interpolateHSV(ref hsv2, ref hsv3, (hmod * 3f) - 1f);
334 else hsv = interpolateHSV(ref hsv3, ref hsv4, (hmod * 3f) - 2f);
335 }
336
337 // Shade the terrain for shadows
338 if (x < 255 && y < 255)
339 {
340 float hfvaluecompare = getHeight(hm, x + 1, y + 1); // light from north-east => look at land height there
341 if (Single.IsInfinity(hfvaluecompare) || Single.IsNaN(hfvaluecompare))
342 hfvaluecompare = 0f;
343
344 float hfdiff = heightvalue - hfvaluecompare; // => positive if NE is lower, negative if here is lower
345 hfdiff *= 0.06f; // some random factor so "it looks good"
346 if (hfdiff > 0.02f)
347 {
348 float highlightfactor = 0.18f;
349 // NE is lower than here
350 // We have to desaturate and lighten the land at the same time
351 hsv.s = (hsv.s - (hfdiff * highlightfactor) > 0f) ? hsv.s - (hfdiff * highlightfactor) : 0f;
352 hsv.v = (hsv.v + (hfdiff * highlightfactor) < 1f) ? hsv.v + (hfdiff * highlightfactor) : 1f;
353 }
354 else if (hfdiff < -0.02f)
355 {
356 // here is lower than NE:
357 // We have to desaturate and blacken the land at the same time
358 hsv.s = (hsv.s + hfdiff > 0f) ? hsv.s + hfdiff : 0f;
359 hsv.v = (hsv.v + hfdiff > 0f) ? hsv.v + hfdiff : 0f;
360 }
361 }
362 mapbmp.SetPixel(x, yr, hsv.toColor());
363 }
364 else
365 {
366 // We're under the water level with the terrain, so paint water instead of land
367
368 heightvalue = waterHeight - heightvalue;
369 if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
370 heightvalue = 0f;
371 else if (heightvalue > 19f)
372 heightvalue = 19f;
373 else if (heightvalue < 0f)
374 heightvalue = 0f;
375
376 heightvalue = 100f - (heightvalue * 100f) / 19f; // 0 - 19 => 100 - 0
377
378 Color water = Color.FromArgb((int)heightvalue, (int)heightvalue, 255);
379 mapbmp.SetPixel(x, yr, water);
380 }
381 }
382 }
383 m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (System.Environment.TickCount - tc) + " ms");
384 }
385 }
386}