diff options
Diffstat (limited to 'OpenSim/Region/Environment/Modules')
-rw-r--r-- | OpenSim/Region/Environment/Modules/World/WorldMap/MapImageModule.cs | 412 |
1 files changed, 409 insertions, 3 deletions
diff --git a/OpenSim/Region/Environment/Modules/World/WorldMap/MapImageModule.cs b/OpenSim/Region/Environment/Modules/World/WorldMap/MapImageModule.cs index 68c8046..a331d37 100644 --- a/OpenSim/Region/Environment/Modules/World/WorldMap/MapImageModule.cs +++ b/OpenSim/Region/Environment/Modules/World/WorldMap/MapImageModule.cs | |||
@@ -26,29 +26,58 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | ||
29 | using System.Drawing; | 30 | using System.Drawing; |
31 | using System.Reflection; | ||
32 | using Axiom.Math; | ||
30 | using Nini.Config; | 33 | using Nini.Config; |
34 | using log4net; | ||
31 | using OpenJPEGNet; | 35 | using OpenJPEGNet; |
32 | using OpenSim.Region.Environment.Interfaces; | 36 | using OpenSim.Region.Environment.Interfaces; |
33 | using OpenSim.Region.Environment.Scenes; | 37 | using OpenSim.Region.Environment.Scenes; |
38 | using libsecondlife; | ||
34 | 39 | ||
35 | namespace OpenSim.Region.Environment.Modules.World.WorldMap | 40 | namespace OpenSim.Region.Environment.Modules.World.WorldMap |
36 | { | 41 | { |
37 | internal class MapImageModule : IMapImageGenerator, IRegionModule | 42 | public class MapImageModule : IMapImageGenerator, IRegionModule |
38 | { | 43 | { |
44 | private static readonly ILog m_log = | ||
45 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
46 | |||
39 | private Scene m_scene; | 47 | private Scene m_scene; |
48 | private IConfigSource m_config; | ||
40 | 49 | ||
41 | #region IMapImageGenerator Members | 50 | #region IMapImageGenerator Members |
42 | 51 | ||
43 | public byte[] WriteJpeg2000Image(string gradientmap) | 52 | public byte[] WriteJpeg2000Image(string gradientmap) |
44 | { | 53 | { |
45 | byte[] imageData = null; | 54 | byte[] imageData = null; |
55 | Bitmap mapbmp = new Bitmap(256, 256); | ||
56 | |||
57 | //Bitmap bmp = TerrainToBitmap(gradientmap); | ||
58 | mapbmp = TerrainToBitmap2(m_scene,mapbmp); | ||
59 | |||
60 | bool drawPrimVolume = true; | ||
61 | |||
62 | try | ||
63 | { | ||
64 | IConfig startupConfig = m_config.Configs["Startup"]; | ||
65 | drawPrimVolume = startupConfig.GetBoolean("DrawPrimOnMapTile", true); | ||
66 | } | ||
67 | catch (Exception) | ||
68 | { | ||
69 | m_log.Warn("Failed to load StartupConfig"); | ||
70 | } | ||
71 | |||
72 | if (drawPrimVolume) | ||
73 | { | ||
74 | DrawObjectVolume(m_scene, mapbmp); | ||
75 | } | ||
46 | 76 | ||
47 | Bitmap bmp = TerrainToBitmap(gradientmap); | ||
48 | 77 | ||
49 | try | 78 | try |
50 | { | 79 | { |
51 | imageData = OpenJPEG.EncodeFromImage(bmp, true); | 80 | imageData = OpenJPEG.EncodeFromImage(mapbmp, true); |
52 | } | 81 | } |
53 | catch (Exception e) // LEGIT: Catching problems caused by OpenJPEG p/invoke | 82 | catch (Exception e) // LEGIT: Catching problems caused by OpenJPEG p/invoke |
54 | { | 83 | { |
@@ -65,6 +94,7 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap | |||
65 | public void Initialise(Scene scene, IConfigSource source) | 94 | public void Initialise(Scene scene, IConfigSource source) |
66 | { | 95 | { |
67 | m_scene = scene; | 96 | m_scene = scene; |
97 | m_config = source; | ||
68 | m_scene.RegisterModuleInterface<IMapImageGenerator>(this); | 98 | m_scene.RegisterModuleInterface<IMapImageGenerator>(this); |
69 | } | 99 | } |
70 | 100 | ||
@@ -128,6 +158,381 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap | |||
128 | } | 158 | } |
129 | } | 159 | } |
130 | 160 | ||
161 | private Bitmap TerrainToBitmap2(Scene whichScene, Bitmap mapbmp) | ||
162 | { | ||
163 | int tc = System.Environment.TickCount; | ||
164 | m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain"); | ||
165 | |||
166 | double[,] hm = whichScene.Heightmap.GetDoubles(); | ||
167 | bool ShadowDebugContinue = true; | ||
168 | //Color prim = Color.FromArgb(120, 120, 120); | ||
169 | //LLVector3 RayEnd = new LLVector3(0, 0, 0); | ||
170 | //LLVector3 RayStart = new LLVector3(0, 0, 0); | ||
171 | //LLVector3 direction = new LLVector3(0, 0, -1); | ||
172 | //Vector3 AXOrigin = new Vector3(); | ||
173 | //Vector3 AXdirection = new Vector3(); | ||
174 | //Ray testRay = new Ray(); | ||
175 | //EntityIntersection rt = new EntityIntersection(); | ||
176 | bool terraincorruptedwarningsaid = false; | ||
177 | |||
178 | float low = 255; | ||
179 | float high = 0; | ||
180 | for (int x = 0; x < 256; x++) | ||
181 | { | ||
182 | for (int y = 0; y < 256; y++) | ||
183 | { | ||
184 | float hmval = (float)hm[x, y]; | ||
185 | if (hmval < low) | ||
186 | low = hmval; | ||
187 | if (hmval > high) | ||
188 | high = hmval; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | float mid = (high + low) * 0.5f; | ||
193 | |||
194 | // temporary initializer | ||
195 | float hfvalue = (float)whichScene.RegionInfo.RegionSettings.WaterHeight; | ||
196 | float hfvaluecompare = hfvalue; | ||
197 | float hfdiff = hfvalue; | ||
198 | int hfdiffi = 0; | ||
199 | |||
200 | |||
201 | for (int x = 0; x < 256; x++) | ||
202 | { | ||
203 | //int tc = System.Environment.TickCount; | ||
204 | for (int y = 0; y < 256; y++) | ||
205 | { | ||
206 | //RayEnd = new LLVector3(x, y, 0); | ||
207 | //RayStart = new LLVector3(x, y, 255); | ||
208 | |||
209 | //direction = LLVector3.Norm(RayEnd - RayStart); | ||
210 | //AXOrigin = new Vector3(RayStart.X, RayStart.Y, RayStart.Z); | ||
211 | //AXdirection = new Vector3(direction.X, direction.Y, direction.Z); | ||
212 | |||
213 | //testRay = new Ray(AXOrigin, AXdirection); | ||
214 | //rt = m_innerScene.GetClosestIntersectingPrim(testRay); | ||
215 | |||
216 | //if (rt.HitTF) | ||
217 | //{ | ||
218 | //mapbmp.SetPixel(x, y, prim); | ||
219 | //} | ||
220 | //else | ||
221 | //{ | ||
222 | //float tmpval = (float)hm[x, y]; | ||
223 | float heightvalue = (float)hm[x, y]; | ||
224 | |||
225 | |||
226 | if (heightvalue > (float)whichScene.RegionInfo.RegionSettings.WaterHeight) | ||
227 | { | ||
228 | |||
229 | // scale height value | ||
230 | heightvalue = low + mid * (heightvalue - low) / mid; | ||
231 | |||
232 | if (heightvalue > 255) | ||
233 | heightvalue = 255; | ||
234 | |||
235 | if (heightvalue < 0) | ||
236 | heightvalue = 0; | ||
237 | |||
238 | if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue)) | ||
239 | heightvalue = 0; | ||
240 | try | ||
241 | { | ||
242 | Color green = Color.FromArgb((int)heightvalue, 100, (int)heightvalue); | ||
243 | |||
244 | // Y flip the cordinates | ||
245 | mapbmp.SetPixel(x, (256 - y) - 1, green); | ||
246 | |||
247 | //X | ||
248 | // . | ||
249 | // | ||
250 | // Shade the terrain for shadows | ||
251 | if ((x - 1 > 0) && (y - 1 > 0)) | ||
252 | { | ||
253 | hfvalue = (float)hm[x, y]; | ||
254 | hfvaluecompare = (float)hm[x - 1, y - 1]; | ||
255 | |||
256 | if (Single.IsInfinity(hfvalue) || Single.IsNaN(hfvalue)) | ||
257 | hfvalue = 0; | ||
258 | |||
259 | if (Single.IsInfinity(hfvaluecompare) || Single.IsNaN(hfvaluecompare)) | ||
260 | hfvaluecompare = 0; | ||
261 | |||
262 | hfdiff = hfvaluecompare - hfvalue; | ||
263 | |||
264 | if (hfdiff > 0.3f) | ||
265 | { | ||
266 | |||
267 | } | ||
268 | else if (hfdiff < -0.3f) | ||
269 | { | ||
270 | // We have to desaturate and blacken the land at the same time | ||
271 | // we use floats, colors use bytes, so shrink are space down to | ||
272 | // 0-255 | ||
273 | |||
274 | |||
275 | try | ||
276 | { | ||
277 | hfdiffi = Math.Abs((int)((hfdiff * 4) + (hfdiff * 0.5))) + 1; | ||
278 | if (hfdiff % 1 != 0) | ||
279 | { | ||
280 | hfdiffi = hfdiffi + Math.Abs((int)(((hfdiff % 1) * 0.5f) * 10f) - 1); | ||
281 | } | ||
282 | } | ||
283 | catch (System.OverflowException) | ||
284 | { | ||
285 | m_log.Debug("[MAPTILE]: Shadow failed at value: " + hfdiff.ToString()); | ||
286 | ShadowDebugContinue = false; | ||
287 | } | ||
288 | |||
289 | if (ShadowDebugContinue) | ||
290 | { | ||
291 | if ((256 - y) - 1 > 0) | ||
292 | { | ||
293 | Color Shade = mapbmp.GetPixel(x - 1, (256 - y) - 1); | ||
294 | |||
295 | int r = Shade.R; | ||
296 | |||
297 | int g = Shade.G; | ||
298 | int b = Shade.B; | ||
299 | Shade = Color.FromArgb((r - hfdiffi > 0) ? r - hfdiffi : 0, (g - hfdiffi > 0) ? g - hfdiffi : 0, (b - hfdiffi > 0) ? b - hfdiffi : 0); | ||
300 | //Console.WriteLine("d:" + hfdiff.ToString() + ", i:" + hfdiffi + ", pos: " + x + "," + y + " - R:" + Shade.R.ToString() + ", G:" + Shade.G.ToString() + ", B:" + Shade.G.ToString()); | ||
301 | mapbmp.SetPixel(x - 1, (256 - y) - 1, Shade); | ||
302 | } | ||
303 | } | ||
304 | |||
305 | |||
306 | } | ||
307 | |||
308 | } | ||
309 | |||
310 | |||
311 | |||
312 | |||
313 | } | ||
314 | catch (System.ArgumentException) | ||
315 | { | ||
316 | if (!terraincorruptedwarningsaid) | ||
317 | { | ||
318 | 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); | ||
319 | terraincorruptedwarningsaid = true; | ||
320 | } | ||
321 | Color black = Color.Black; | ||
322 | mapbmp.SetPixel(x, (256 - y) - 1, black); | ||
323 | } | ||
324 | } | ||
325 | else | ||
326 | { | ||
327 | // Y flip the cordinates | ||
328 | heightvalue = (float)whichScene.RegionInfo.RegionSettings.WaterHeight - heightvalue; | ||
329 | if (heightvalue > 19) | ||
330 | heightvalue = 19; | ||
331 | if (heightvalue < 0) | ||
332 | heightvalue = 0; | ||
333 | |||
334 | heightvalue = 100 - (heightvalue * 100) / 19; | ||
335 | |||
336 | if (heightvalue > 255) | ||
337 | heightvalue = 255; | ||
338 | |||
339 | if (heightvalue < 0) | ||
340 | heightvalue = 0; | ||
341 | |||
342 | if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue)) | ||
343 | heightvalue = 0; | ||
344 | |||
345 | try | ||
346 | { | ||
347 | Color water = Color.FromArgb((int)heightvalue, (int)heightvalue, 255); | ||
348 | mapbmp.SetPixel(x, (256 - y) - 1, water); | ||
349 | } | ||
350 | catch (System.ArgumentException) | ||
351 | { | ||
352 | if (!terraincorruptedwarningsaid) | ||
353 | { | ||
354 | 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); | ||
355 | terraincorruptedwarningsaid = true; | ||
356 | } | ||
357 | Color black = Color.Black; | ||
358 | mapbmp.SetPixel(x, (256 - y) - 1, black); | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | //} | ||
363 | |||
364 | //tc = System.Environment.TickCount - tc; | ||
365 | //m_log.Info("[MAPTILE]: Completed One row in " + tc + " ms"); | ||
366 | } | ||
367 | m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (System.Environment.TickCount - tc) + " ms"); | ||
368 | |||
369 | return mapbmp; | ||
370 | } | ||
371 | |||
372 | |||
373 | private Bitmap DrawObjectVolume(Scene whichScene, Bitmap mapbmp) | ||
374 | { | ||
375 | int tc = 0; | ||
376 | double[,] hm = whichScene.Heightmap.GetDoubles(); | ||
377 | tc = System.Environment.TickCount; | ||
378 | m_log.Info("[MAPTILE]: Generating Maptile Step 2: Object Volume Profile"); | ||
379 | List<EntityBase> objs = whichScene.GetEntities(); | ||
380 | |||
381 | lock (objs) | ||
382 | { | ||
383 | foreach (EntityBase obj in objs) | ||
384 | { | ||
385 | // Only draw the contents of SceneObjectGroup | ||
386 | if (obj is SceneObjectGroup) | ||
387 | { | ||
388 | SceneObjectGroup mapdot = (SceneObjectGroup)obj; | ||
389 | Color mapdotspot = Color.Gray; // Default color when prim color is white | ||
390 | // Loop over prim in group | ||
391 | foreach (SceneObjectPart part in mapdot.Children.Values) | ||
392 | { | ||
393 | if (part == null) | ||
394 | continue; | ||
395 | |||
396 | |||
397 | // Draw if the object is at least 1 meter wide in any direction | ||
398 | if (part.Scale.X > 1f || part.Scale.Y > 1f || part.Scale.Z > 1f) | ||
399 | { | ||
400 | // Try to get the RGBA of the default texture entry.. | ||
401 | // | ||
402 | try | ||
403 | { | ||
404 | if (part == null) | ||
405 | continue; | ||
406 | |||
407 | if (part.Shape == null) | ||
408 | continue; | ||
409 | |||
410 | if (part.Shape.PCode == (byte)PCode.Tree || part.Shape.PCode == (byte)PCode.NewTree) | ||
411 | continue; // eliminates trees from this since we don't really have a good tree representation | ||
412 | // if you want tree blocks on the map comment the above line and uncomment the below line | ||
413 | //mapdotspot = Color.PaleGreen; | ||
414 | |||
415 | if (part.Shape.Textures == null) | ||
416 | continue; | ||
417 | |||
418 | if (part.Shape.Textures.DefaultTexture == null) | ||
419 | continue; | ||
420 | |||
421 | LLColor texcolor = part.Shape.Textures.DefaultTexture.RGBA; | ||
422 | |||
423 | // Not sure why some of these are null, oh well. | ||
424 | |||
425 | int colorr = 255 - (int)(texcolor.R * 255f); | ||
426 | int colorg = 255 - (int)(texcolor.G * 255f); | ||
427 | int colorb = 255 - (int)(texcolor.B * 255f); | ||
428 | |||
429 | if (!(colorr == 255 && colorg == 255 && colorb == 255)) | ||
430 | { | ||
431 | //Try to set the map spot color | ||
432 | try | ||
433 | { | ||
434 | // If the color gets goofy somehow, skip it *shakes fist at LLColor | ||
435 | mapdotspot = Color.FromArgb(colorr, colorg, colorb); | ||
436 | } | ||
437 | catch (ArgumentException) | ||
438 | { | ||
439 | } | ||
440 | } | ||
441 | } | ||
442 | catch (IndexOutOfRangeException) | ||
443 | { | ||
444 | // Windows Array | ||
445 | } | ||
446 | catch (ArgumentOutOfRangeException) | ||
447 | { | ||
448 | // Mono Array | ||
449 | } | ||
450 | |||
451 | LLVector3 pos = part.GetWorldPosition(); | ||
452 | |||
453 | // skip prim outside of retion | ||
454 | if (pos.X < 0f || pos.X > 256f || pos.Y < 0f || pos.Y > 256f) | ||
455 | continue; | ||
456 | |||
457 | // skip prim in non-finite position | ||
458 | if (Single.IsNaN(pos.X) || Single.IsNaN(pos.Y) || Single.IsInfinity(pos.X) | ||
459 | || Single.IsInfinity(pos.Y)) | ||
460 | continue; | ||
461 | |||
462 | // Figure out if object is under 256m above the height of the terrain | ||
463 | bool isBelow256AboveTerrain = false; | ||
464 | |||
465 | try | ||
466 | { | ||
467 | isBelow256AboveTerrain = (pos.Z < ((float)hm[(int)pos.X, (int)pos.Y] + 256f)); | ||
468 | } | ||
469 | catch (Exception) | ||
470 | { | ||
471 | } | ||
472 | |||
473 | if (isBelow256AboveTerrain) | ||
474 | { | ||
475 | // Translate scale by rotation so scale is represented properly when object is rotated | ||
476 | Vector3 scale = new Vector3(part.Shape.Scale.X, part.Shape.Scale.Y, part.Shape.Scale.Z); | ||
477 | LLQuaternion llrot = part.GetWorldRotation(); | ||
478 | Quaternion rot = new Quaternion(llrot.W, llrot.X, llrot.Y, llrot.Z); | ||
479 | scale = rot * scale; | ||
480 | |||
481 | // negative scales don't work in this situation | ||
482 | scale.x = Math.Abs(scale.x); | ||
483 | scale.y = Math.Abs(scale.y); | ||
484 | scale.z = Math.Abs(scale.z); | ||
485 | |||
486 | // This scaling isn't very accurate and doesn't take into account the face rotation :P | ||
487 | int mapdrawstartX = (int)(pos.X - scale.x); | ||
488 | int mapdrawstartY = (int)(pos.Y - scale.y); | ||
489 | int mapdrawendX = (int)(pos.X + scale.x); | ||
490 | int mapdrawendY = (int)(pos.Y + scale.y); | ||
491 | |||
492 | // If object is beyond the edge of the map, don't draw it to avoid errors | ||
493 | if (mapdrawstartX < 0 || mapdrawstartX > 255 || mapdrawendX < 0 || mapdrawendX > 255 | ||
494 | || mapdrawstartY < 0 || mapdrawstartY > 255 || mapdrawendY < 0 | ||
495 | || mapdrawendY > 255) | ||
496 | continue; | ||
497 | |||
498 | int wy = 0; | ||
499 | |||
500 | bool breakYN = false; // If we run into an error drawing, break out of the | ||
501 | // loop so we don't lag to death on error handling | ||
502 | for (int wx = mapdrawstartX; wx < mapdrawendX; wx++) | ||
503 | { | ||
504 | for (wy = mapdrawstartY; wy < mapdrawendY; wy++) | ||
505 | { | ||
506 | //m_log.InfoFormat("[MAPDEBUG]: {0},{1}({2})", wx, (255 - wy),wy); | ||
507 | try | ||
508 | { | ||
509 | // Remember, flip the y! | ||
510 | mapbmp.SetPixel(wx, (255 - wy), mapdotspot); | ||
511 | } | ||
512 | catch (ArgumentException) | ||
513 | { | ||
514 | breakYN = true; | ||
515 | } | ||
516 | |||
517 | if (breakYN) | ||
518 | break; | ||
519 | } | ||
520 | |||
521 | if (breakYN) | ||
522 | break; | ||
523 | } | ||
524 | } // Object is within 256m Z of terrain | ||
525 | } // object is at least a meter wide | ||
526 | } // loop over group children | ||
527 | } // entitybase is sceneobject group | ||
528 | } // foreach loop over entities | ||
529 | } // lock entities objs | ||
530 | |||
531 | m_log.Info("[MAPTILE]: Generating Maptile Step 2: Done in " + (System.Environment.TickCount - tc) + " ms"); | ||
532 | return mapbmp; | ||
533 | } | ||
534 | |||
535 | # region Depreciated Maptile Generation. Adam may update this | ||
131 | private Bitmap TerrainToBitmap(string gradientmap) | 536 | private Bitmap TerrainToBitmap(string gradientmap) |
132 | { | 537 | { |
133 | Bitmap gradientmapLd = new Bitmap(gradientmap); | 538 | Bitmap gradientmapLd = new Bitmap(gradientmap); |
@@ -163,5 +568,6 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap | |||
163 | return bmp; | 568 | return bmp; |
164 | } | 569 | } |
165 | } | 570 | } |
571 | #endregion | ||
166 | } | 572 | } |
167 | } \ No newline at end of file | 573 | } \ No newline at end of file |