aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Warp3DImageModule.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Warp3DImageModule.cs1073
1 files changed, 1073 insertions, 0 deletions
diff --git a/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Warp3DImageModule.cs b/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Warp3DImageModule.cs
new file mode 100644
index 0000000..8e50a16
--- /dev/null
+++ b/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Warp3DImageModule.cs
@@ -0,0 +1,1073 @@
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 OpenSimulator 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 System.Reflection;
34using System.Runtime;
35
36using CSJ2K;
37using Nini.Config;
38using log4net;
39using Warp3D;
40using Mono.Addins;
41
42using OpenSim.Framework;
43using OpenSim.Region.Framework.Interfaces;
44using OpenSim.Region.Framework.Scenes;
45using OpenSim.Region.PhysicsModules.SharedBase;
46using OpenSim.Services.Interfaces;
47
48using OpenMetaverse;
49using OpenMetaverse.Assets;
50using OpenMetaverse.Imaging;
51using OpenMetaverse.Rendering;
52using OpenMetaverse.StructuredData;
53
54using WarpRenderer = Warp3D.Warp3D;
55using System.Drawing.Drawing2D;
56
57[assembly: Addin("Warp3DCachedImageModule", "1.1")]
58[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
59namespace OpenSim.Region.CoreModules.World.Warp3DMap
60{
61 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "Warp3DCachedImageModule")]
62 public class Warp3DImageModule : IMapImageGenerator, INonSharedRegionModule
63 {
64 private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 216);
65 // private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 128);
66
67 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
68
69#pragma warning disable 414
70 private static string LogHeader = "[WARP 3D CACHED IMAGE MODULE]";
71#pragma warning restore 414
72 private const float m_cameraHeight = 4096f;
73
74 internal Scene m_scene;
75 private IRendering m_primMesher;
76 internal IJ2KDecoder m_imgDecoder;
77
78 // caches per rendering
79 private Dictionary<string, warp_Texture> m_warpTextures = new Dictionary<string, warp_Texture>();
80 private Dictionary<UUID, int> m_colors = new Dictionary<UUID, int>();
81
82 private IConfigSource m_config;
83 private bool m_drawPrimVolume = true; // true if should render the prims on the tile
84 private bool m_textureTerrain = true; // true if to create terrain splatting texture
85 private bool m_textureAverageTerrain = false; // replace terrain textures by their average color
86 private bool m_texturePrims = true; // true if should texture the rendered prims
87 private float m_texturePrimSize = 48f; // size of prim before we consider texturing it
88 private bool m_renderMeshes = false; // true if to render meshes rather than just bounding boxes
89 private float m_renderMinHeight = -100f;
90 private float m_renderMaxHeight = 4096f;
91
92 private String m_cacheDirectory = "";
93 private bool m_enable_date = false;
94 private bool m_enable_regionName = false;
95 private bool m_enable_regionPosition = false;
96 private bool m_enable_refreshEveryMonth = false;
97 private bool m_enable_HostedBy = false;
98 private String m_enable_HostedByText = "";
99
100 private bool m_Enabled = false;
101
102 // private Bitmap lastImage = null;
103 private DateTime lastImageTime = DateTime.MinValue;
104
105 #region Region Module interface
106
107 public void Initialise(IConfigSource source)
108 {
109 m_config = source;
110
111 string[] configSections = new string[] { "Map", "Startup" };
112
113 if (Util.GetConfigVarFromSections<string>(
114 m_config, "MapImageModule", configSections, "MapImageModule") != "Warp3DCachedImageModule")
115 return;
116
117 m_Enabled = true;
118
119 m_drawPrimVolume =
120 Util.GetConfigVarFromSections<bool>(m_config, "DrawPrimOnMapTile", configSections, m_drawPrimVolume);
121 m_textureTerrain =
122 Util.GetConfigVarFromSections<bool>(m_config, "TextureOnMapTile", configSections, m_textureTerrain);
123 m_textureAverageTerrain =
124 Util.GetConfigVarFromSections<bool>(m_config, "AverageTextureColorOnMapTile", configSections, m_textureAverageTerrain);
125 if (m_textureAverageTerrain)
126 m_textureTerrain = true;
127 m_texturePrims =
128 Util.GetConfigVarFromSections<bool>(m_config, "TexturePrims", configSections, m_texturePrims);
129 m_texturePrimSize =
130 Util.GetConfigVarFromSections<float>(m_config, "TexturePrimSize", configSections, m_texturePrimSize);
131 m_renderMeshes =
132 Util.GetConfigVarFromSections<bool>(m_config, "RenderMeshes", configSections, m_renderMeshes);
133 m_cacheDirectory =
134 Util.GetConfigVarFromSections<string>(m_config, "CacheDirectory", configSections, System.IO.Path.Combine(new DirectoryInfo(".").FullName, "MapImageCache"));
135
136
137 m_enable_date = Util.GetConfigVarFromSections<bool>(m_config, "enableDate", configSections, false);
138 m_enable_regionName = Util.GetConfigVarFromSections<bool>(m_config, "enableName", configSections, false);
139 m_enable_regionPosition = Util.GetConfigVarFromSections<bool>(m_config, "enablePosition", configSections, false);
140 m_enable_refreshEveryMonth = Util.GetConfigVarFromSections<bool>(m_config, "RefreshEveryMonth", configSections, true);
141 m_enable_HostedBy = Util.GetConfigVarFromSections<bool>(m_config, "enableHostedBy", configSections, false);
142 m_enable_HostedByText = Util.GetConfigVarFromSections<String>(m_config, "HosterText", configSections, String.Empty);
143
144 m_renderMaxHeight = Util.GetConfigVarFromSections<float>(m_config, "RenderMaxHeight", configSections, m_renderMaxHeight);
145 m_renderMinHeight = Util.GetConfigVarFromSections<float>(m_config, "RenderMinHeight", configSections, m_renderMinHeight);
146
147 if (!Directory.Exists(m_cacheDirectory))
148 Directory.CreateDirectory(m_cacheDirectory);
149
150 if (m_renderMaxHeight < 100f)
151 m_renderMaxHeight = 100f;
152 else if (m_renderMaxHeight > m_cameraHeight - 10f)
153 m_renderMaxHeight = m_cameraHeight - 10f;
154
155 if (m_renderMinHeight < -100f)
156 m_renderMinHeight = -100f;
157 else if (m_renderMinHeight > m_renderMaxHeight - 10f)
158 m_renderMinHeight = m_renderMaxHeight - 10f;
159 }
160
161 public void AddRegion(Scene scene)
162 {
163 if (!m_Enabled)
164 return;
165
166 m_scene = scene;
167
168 List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
169 if (renderers.Count > 0)
170 m_log.Info("[MAPTILE]: Loaded prim mesher " + renderers[0]);
171 else
172 m_log.Info("[MAPTILE]: No prim mesher loaded, prim rendering will be disabled");
173
174 m_scene.RegisterModuleInterface<IMapImageGenerator>(this);
175 }
176
177 public void RegionLoaded(Scene scene)
178 {
179 if (!m_Enabled)
180 return;
181
182 m_imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
183 }
184
185 public void RemoveRegion(Scene scene)
186 {
187 }
188
189 public void Close()
190 {
191 }
192
193 public string Name
194 {
195 get { return "Warp3DImageModule"; }
196 }
197
198 public Type ReplaceableInterface
199 {
200 get { return null; }
201 }
202
203 #endregion
204
205 #region IMapImageGenerator Members
206
207 private Vector3 cameraPos;
208 private Vector3 cameraDir;
209 private int viewWitdh = 256;
210 private int viewHeight = 256;
211 private float fov;
212 private bool orto;
213
214 public static string fillInt(int _i, int _l)
215 {
216 String _return = _i.ToString();
217
218 while (_return.Length < _l)
219 {
220 _return = 0 + _return;
221 }
222
223 return _return;
224 }
225
226 public static int getCurrentUnixTime()
227 {
228 return (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
229 }
230
231 public static String unixTimeToDateString(int unixTime)
232 {
233 DateTime unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
234 long unixTimeStampInTicks = (long)(unixTime * TimeSpan.TicksPerSecond);
235 DateTime _date = new DateTime(unixStart.Ticks + unixTimeStampInTicks, System.DateTimeKind.Utc);
236
237 return fillInt(_date.Day, 2) + "." + fillInt(_date.Month, 2) + "." + fillInt(_date.Year, 4) + " " + fillInt(_date.Hour, 2) + ":" + fillInt(_date.Minute, 2);
238 }
239
240 private void writeDateOnMap(ref Bitmap _map)
241 {
242 RectangleF rectf = new RectangleF(2, 1, 200, 25);
243
244 Graphics g = Graphics.FromImage(_map);
245 g.SmoothingMode = SmoothingMode.AntiAlias;
246 g.InterpolationMode = InterpolationMode.HighQualityBicubic;
247 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
248 g.DrawString(unixTimeToDateString(getCurrentUnixTime()), new Font("Arial", 8), Brushes.White, rectf);
249 g.Flush();
250 }
251
252 private void writeNameOnMap(ref Bitmap _map)
253 {
254 RectangleF rectf = new RectangleF(2, m_scene.RegionInfo.RegionSizeX - 15, 200, 25);
255
256 Graphics g = Graphics.FromImage(_map);
257 g.SmoothingMode = SmoothingMode.AntiAlias;
258 g.InterpolationMode = InterpolationMode.HighQualityBicubic;
259 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
260 g.DrawString(m_scene.Name, new Font("Arial", 8), Brushes.White, rectf);
261 g.Flush();
262 }
263
264 private void writePositionOnMap(ref Bitmap _map)
265 {
266 RectangleF rectf = new RectangleF(m_scene.RegionInfo.RegionSizeY - 85, m_scene.RegionInfo.RegionSizeX - 15, 80, 25);
267
268 Graphics g = Graphics.FromImage(_map);
269 g.SmoothingMode = SmoothingMode.AntiAlias;
270 g.InterpolationMode = InterpolationMode.HighQualityBicubic;
271 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
272 g.DrawString(m_scene.RegionInfo.RegionLocX + ", " + m_scene.RegionInfo.RegionLocY, new Font("Arial", 8), Brushes.White, rectf);
273 g.Flush();
274 }
275
276 private void writeHostedByOnMap(ref Bitmap _map)
277 {
278 RectangleF rectf = new RectangleF(2, m_scene.RegionInfo.RegionSizeX - 15, 200, 25);
279
280 Graphics g = Graphics.FromImage(_map);
281 g.SmoothingMode = SmoothingMode.AntiAlias;
282 g.InterpolationMode = InterpolationMode.HighQualityBicubic;
283 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
284 g.DrawString(m_enable_HostedByText, new Font("Arial", 8), Brushes.Gray, rectf);
285 g.Flush();
286 }
287
288
289 public Bitmap CreateMapTile()
290 {
291 if ((File.GetCreationTime(System.IO.Path.Combine(m_cacheDirectory, m_scene.RegionInfo.RegionID + ".bmp")).Month != DateTime.Now.Month) && m_enable_refreshEveryMonth == true)
292 File.Delete(System.IO.Path.Combine(m_cacheDirectory, m_scene.RegionInfo.RegionID + ".bmp"));
293
294 if (File.Exists(System.IO.Path.Combine(m_cacheDirectory, m_scene.RegionInfo.RegionID + ".bmp")))
295 {
296 return new Bitmap(System.IO.Path.Combine(m_cacheDirectory, m_scene.RegionInfo.RegionID + ".bmp"));
297 }
298 else
299 {
300 List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
301 if (renderers.Count > 0)
302 {
303 m_primMesher = RenderingLoader.LoadRenderer(renderers[0]);
304 }
305
306 cameraPos = new Vector3(
307 (m_scene.RegionInfo.RegionSizeX) * 0.5f,
308 (m_scene.RegionInfo.RegionSizeY) * 0.5f,
309 m_cameraHeight);
310
311 cameraDir = -Vector3.UnitZ;
312 viewWitdh = (int)m_scene.RegionInfo.RegionSizeX;
313 viewHeight = (int)m_scene.RegionInfo.RegionSizeY;
314 orto = true;
315
316 // fov = warp_Math.rad2deg(2f * (float)Math.Atan2(viewWitdh, 4096f));
317 // orto = false;
318
319 Bitmap tile = GenImage();
320
321 if (m_enable_date)
322 writeDateOnMap(ref tile);
323
324 if (m_enable_regionName)
325 writeNameOnMap(ref tile);
326
327 if (m_enable_regionPosition)
328 writePositionOnMap(ref tile);
329
330 if (m_enable_HostedBy)
331 writeHostedByOnMap(ref tile);
332
333 tile.Save(System.IO.Path.Combine(m_cacheDirectory, m_scene.RegionInfo.RegionID + ".bmp"));
334
335 // image may be reloaded elsewhere, so no compression format
336 string filename = "MAP-" + m_scene.RegionInfo.RegionID.ToString() + ".png";
337 tile.Save(filename, ImageFormat.Png);
338 m_primMesher = null;
339 return tile;
340 }
341 }
342
343 public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float pfov, int width, int height, bool useTextures)
344 {
345 List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
346 if (renderers.Count > 0)
347 {
348 m_primMesher = RenderingLoader.LoadRenderer(renderers[0]);
349 }
350
351 cameraPos = camPos;
352 cameraDir = camDir;
353 viewWitdh = width;
354 viewHeight = height;
355 fov = pfov;
356 orto = false;
357
358 Bitmap tile = GenImage();
359 m_primMesher = null;
360 return tile;
361 }
362
363 private Bitmap GenImage()
364 {
365 m_colors.Clear();
366 m_warpTextures.Clear();
367
368 WarpRenderer renderer = new WarpRenderer();
369
370 if (!renderer.CreateScene(viewWitdh, viewHeight))
371 return new Bitmap(viewWitdh, viewHeight);
372
373 #region Camera
374
375 warp_Vector pos = ConvertVector(cameraPos);
376 warp_Vector lookat = warp_Vector.add(pos, ConvertVector(cameraDir));
377
378 if (orto)
379 renderer.Scene.defaultCamera.setOrthographic(true, viewWitdh, viewHeight);
380 else
381 renderer.Scene.defaultCamera.setFov(fov);
382
383 renderer.Scene.defaultCamera.setPos(pos);
384 renderer.Scene.defaultCamera.lookAt(lookat);
385 #endregion Camera
386
387 renderer.Scene.setAmbient(warp_Color.getColor(192, 191, 173));
388 renderer.Scene.addLight("Light1", new warp_Light(new warp_Vector(0f, 1f, 8f), warp_Color.White, 0, 320, 40));
389
390 CreateWater(renderer);
391 CreateTerrain(renderer);
392 if (m_drawPrimVolume)
393 CreateAllPrims(renderer);
394
395 renderer.Render();
396 Bitmap bitmap = renderer.Scene.getImage();
397
398 renderer.Scene.destroy();
399 renderer.Reset();
400 renderer = null;
401
402 m_colors.Clear();
403 m_warpTextures.Clear();
404
405 GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
406 GC.Collect();
407 GC.WaitForPendingFinalizers();
408 GC.Collect();
409 GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.Default;
410 return bitmap;
411 }
412
413 public byte[] WriteJpeg2000Image()
414 {
415 try
416 {
417 using (Bitmap mapbmp = CreateMapTile())
418 return OpenJPEG.EncodeFromImage(mapbmp, false);
419 }
420 catch (Exception e)
421 {
422 // JPEG2000 encoder failed
423 m_log.Error("[WARP 3D IMAGE MODULE]: Failed generating terrain map: ", e);
424 }
425
426 return null;
427 }
428
429 #endregion
430
431 #region Rendering Methods
432
433 // Add a water plane to the renderer.
434 private void CreateWater(WarpRenderer renderer)
435 {
436 float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
437
438 renderer.AddPlane("Water", m_scene.RegionInfo.RegionSizeX * 0.5f);
439 renderer.Scene.sceneobject("Water").setPos(m_scene.RegionInfo.RegionSizeX * 0.5f,
440 waterHeight,
441 m_scene.RegionInfo.RegionSizeY * 0.5f);
442
443 warp_Material waterMaterial = new warp_Material(ConvertColor(WATER_COLOR));
444 renderer.Scene.addMaterial("WaterMat", waterMaterial);
445 renderer.SetObjectMaterial("Water", "WaterMat");
446 }
447
448 // Add a terrain to the renderer.
449 // Note that we create a 'low resolution' 257x257 vertex terrain rather than trying for
450 // full resolution. This saves a lot of memory especially for very large regions.
451 private void CreateTerrain(WarpRenderer renderer)
452 {
453 ITerrainChannel terrain = m_scene.Heightmap;
454
455 float regionsx = m_scene.RegionInfo.RegionSizeX;
456 float regionsy = m_scene.RegionInfo.RegionSizeY;
457
458 // 'diff' is the difference in scale between the real region size and the size of terrain we're buiding
459
460 int bitWidth;
461 int bitHeight;
462
463 const double log2inv = 1.4426950408889634073599246810019;
464 bitWidth = (int)Math.Ceiling((Math.Log(terrain.Width) * log2inv));
465 bitHeight = (int)Math.Ceiling((Math.Log(terrain.Height) * log2inv));
466
467 if (bitWidth > 8) // more than 256 is very heavy :(
468 bitWidth = 8;
469 if (bitHeight > 8)
470 bitHeight = 8;
471
472 int twidth = (int)Math.Pow(2, bitWidth);
473 int theight = (int)Math.Pow(2, bitHeight);
474
475 float diff = regionsx / twidth;
476
477 int npointsx = (int)(regionsx / diff);
478 int npointsy = (int)(regionsy / diff);
479
480 float invsx = 1.0f / (npointsx * diff);
481 float invsy = 1.0f / (npointsy * diff);
482
483 npointsx++;
484 npointsy++;
485
486 // Create all the vertices for the terrain
487 warp_Object obj = new warp_Object();
488 warp_Vector pos;
489 float x, y;
490 float tv;
491 for (y = 0; y < regionsy; y += diff)
492 {
493 tv = y * invsy;
494 for (x = 0; x < regionsx; x += diff)
495 {
496 pos = ConvertVector(x, y, (float)terrain[(int)x, (int)y]);
497 obj.addVertex(new warp_Vertex(pos, x * invsx, tv));
498 }
499 pos = ConvertVector(x, y, (float)terrain[(int)(x - diff), (int)y]);
500 obj.addVertex(new warp_Vertex(pos, 1.0f, tv));
501 }
502
503 int lastY = (int)(y - diff);
504 for (x = 0; x < regionsx; x += diff)
505 {
506 pos = ConvertVector(x, y, (float)terrain[(int)x, lastY]);
507 obj.addVertex(new warp_Vertex(pos, x * invsx, 1.0f));
508 }
509 pos = ConvertVector(x, y, (float)terrain[(int)(x - diff), lastY]);
510 obj.addVertex(new warp_Vertex(pos, 1.0f, 1.0f));
511
512 // create triangles.
513 int limx = npointsx - 1;
514 int limy = npointsy - 1;
515 for (int j = 0; j < limy; j++)
516 {
517 for (int i = 0; i < limx; i++)
518 {
519 int v = j * npointsx + i;
520
521 // Make two triangles for each of the squares in the grid of vertices
522 obj.addTriangle(
523 v,
524 v + 1,
525 v + npointsx);
526
527 obj.addTriangle(
528 v + npointsx + 1,
529 v + npointsx,
530 v + 1);
531 }
532 }
533
534 renderer.Scene.addObject("Terrain", obj);
535
536 UUID[] textureIDs = new UUID[4];
537 float[] startHeights = new float[4];
538 float[] heightRanges = new float[4];
539
540 OpenSim.Framework.RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings;
541
542 textureIDs[0] = regionInfo.TerrainTexture1;
543 textureIDs[1] = regionInfo.TerrainTexture2;
544 textureIDs[2] = regionInfo.TerrainTexture3;
545 textureIDs[3] = regionInfo.TerrainTexture4;
546
547 startHeights[0] = (float)regionInfo.Elevation1SW;
548 startHeights[1] = (float)regionInfo.Elevation1NW;
549 startHeights[2] = (float)regionInfo.Elevation1SE;
550 startHeights[3] = (float)regionInfo.Elevation1NE;
551
552 heightRanges[0] = (float)regionInfo.Elevation2SW;
553 heightRanges[1] = (float)regionInfo.Elevation2NW;
554 heightRanges[2] = (float)regionInfo.Elevation2SE;
555 heightRanges[3] = (float)regionInfo.Elevation2NE;
556
557 warp_Texture texture;
558 using (Bitmap image = TerrainSplat.Splat(terrain, textureIDs, startHeights, heightRanges,
559 m_scene.RegionInfo.WorldLocX, m_scene.RegionInfo.WorldLocY,
560 m_scene.AssetService, m_imgDecoder, m_textureTerrain, m_textureAverageTerrain,
561 twidth, twidth))
562 texture = new warp_Texture(image);
563
564 warp_Material material = new warp_Material(texture);
565 renderer.Scene.addMaterial("TerrainMat", material);
566 renderer.SetObjectMaterial("Terrain", "TerrainMat");
567 }
568
569 private void CreateAllPrims(WarpRenderer renderer)
570 {
571 if (m_primMesher == null)
572 return;
573
574 m_scene.ForEachSOG(
575 delegate (SceneObjectGroup group)
576 {
577 foreach (SceneObjectPart child in group.Parts)
578 CreatePrim(renderer, child);
579 }
580 );
581 }
582
583 private void UVPlanarMap(Vertex v, Vector3 scale, out float tu, out float tv)
584 {
585 Vector3 scaledPos = v.Position * scale;
586 float d = v.Normal.X;
587 if (d >= 0.5f)
588 {
589 tu = 2f * scaledPos.Y;
590 tv = scaledPos.X * v.Normal.Z - scaledPos.Z * v.Normal.X;
591 }
592 else if (d <= -0.5f)
593 {
594 tu = -2f * scaledPos.Y;
595 tv = -scaledPos.X * v.Normal.Z + scaledPos.Z * v.Normal.X;
596 }
597 else if (v.Normal.Y > 0f)
598 {
599 tu = -2f * scaledPos.X;
600 tv = scaledPos.Y * v.Normal.Z - scaledPos.Z * v.Normal.Y;
601 }
602 else
603 {
604 tu = 2f * scaledPos.X;
605 tv = -scaledPos.Y * v.Normal.Z + scaledPos.Z * v.Normal.Y;
606 }
607
608 tv *= 2f;
609 }
610
611 private void CreatePrim(WarpRenderer renderer, SceneObjectPart prim)
612 {
613 if ((PCode)prim.Shape.PCode != PCode.Prim)
614 return;
615
616 Vector3 ppos = prim.GetWorldPosition();
617 if (ppos.Z < m_renderMinHeight || ppos.Z > m_renderMaxHeight)
618 return;
619
620 warp_Vector primPos = ConvertVector(ppos);
621 warp_Quaternion primRot = ConvertQuaternion(prim.GetWorldRotation());
622 warp_Matrix m = warp_Matrix.quaternionMatrix(primRot);
623
624 float screenFactor = renderer.Scene.EstimateBoxProjectedArea(primPos, ConvertVector(prim.Scale), m);
625 if (screenFactor < 0)
626 return;
627
628 int p2 = (int)(-(float)Math.Log(screenFactor) * 1.442695f * 0.5 - 1);
629
630 if (p2 < 0)
631 p2 = 0;
632 else if (p2 > 3)
633 p2 = 3;
634
635 DetailLevel lod = (DetailLevel)(3 - p2);
636
637 FacetedMesh renderMesh = null;
638 Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset);
639
640 if (m_renderMeshes)
641 {
642 if (omvPrim.Sculpt != null && omvPrim.Sculpt.SculptTexture != UUID.Zero)
643 {
644 // Try fetchinng the asset
645 byte[] sculptAsset = m_scene.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString());
646 if (sculptAsset != null)
647 {
648 // Is it a mesh?
649 if (omvPrim.Sculpt.Type == SculptType.Mesh)
650 {
651 AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset);
652 FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, lod, out renderMesh);
653 meshAsset = null;
654 }
655 else // It's sculptie
656 {
657 if (m_imgDecoder != null)
658 {
659 Image sculpt = m_imgDecoder.DecodeToImage(sculptAsset);
660 if (sculpt != null)
661 {
662 renderMesh = m_primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt, lod);
663 sculpt.Dispose();
664 }
665 }
666 }
667 }
668 else
669 {
670 m_log.WarnFormat("[Warp3D] failed to get mesh or sculpt asset {0} of prim {1} at {2}",
671 omvPrim.Sculpt.SculptTexture.ToString(), prim.Name, prim.GetWorldPosition().ToString());
672 }
673 }
674 }
675
676 // If not a mesh or sculptie, try the regular mesher
677 if (renderMesh == null)
678 {
679 renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, lod);
680 }
681
682 if (renderMesh == null)
683 return;
684
685 string primID = prim.UUID.ToString();
686
687 // Create the prim faces
688 // TODO: Implement the useTextures flag behavior
689 for (int i = 0; i < renderMesh.Faces.Count; i++)
690 {
691 Face face = renderMesh.Faces[i];
692 string meshName = primID + i.ToString();
693
694 // Avoid adding duplicate meshes to the scene
695 if (renderer.Scene.objectData.ContainsKey(meshName))
696 continue;
697
698 warp_Object faceObj = new warp_Object();
699
700 Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i);
701 Color4 faceColor = teFace.RGBA;
702 if (faceColor.A == 0)
703 continue;
704
705 string materialName = String.Empty;
706 if (m_texturePrims)
707 {
708 // if(lod > DetailLevel.Low)
709 {
710 // materialName = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID, lod == DetailLevel.Low);
711 materialName = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID, false, prim);
712 if (String.IsNullOrEmpty(materialName))
713 continue;
714 int c = renderer.Scene.material(materialName).getColor();
715 if ((c & warp_Color.MASKALPHA) == 0)
716 continue;
717 }
718 }
719 else
720 materialName = GetOrCreateMaterial(renderer, faceColor);
721
722 if (renderer.Scene.material(materialName).getTexture() == null)
723 {
724 // uv map details dont not matter for color;
725 for (int j = 0; j < face.Vertices.Count; j++)
726 {
727 Vertex v = face.Vertices[j];
728 warp_Vector pos = ConvertVector(v.Position);
729 warp_Vertex vert = new warp_Vertex(pos, v.TexCoord.X, v.TexCoord.Y);
730 faceObj.addVertex(vert);
731 }
732 }
733 else
734 {
735 float tu;
736 float tv;
737 float offsetu = teFace.OffsetU + 0.5f;
738 float offsetv = teFace.OffsetV + 0.5f;
739 float scaleu = teFace.RepeatU;
740 float scalev = teFace.RepeatV;
741 float rotation = teFace.Rotation;
742 float rc = 0;
743 float rs = 0;
744 if (rotation != 0)
745 {
746 rc = (float)Math.Cos(rotation);
747 rs = (float)Math.Sin(rotation);
748 }
749
750 for (int j = 0; j < face.Vertices.Count; j++)
751 {
752 warp_Vertex vert;
753 Vertex v = face.Vertices[j];
754 warp_Vector pos = ConvertVector(v.Position);
755 if (teFace.TexMapType == MappingType.Planar)
756 UVPlanarMap(v, prim.Scale, out tu, out tv);
757 else
758 {
759 tu = v.TexCoord.X - 0.5f;
760 tv = 0.5f - v.TexCoord.Y;
761 }
762 if (rotation != 0)
763 {
764 float tur = tu * rc - tv * rs;
765 float tvr = tu * rs + tv * rc;
766 tur *= scaleu;
767 tur += offsetu;
768
769 tvr *= scalev;
770 tvr += offsetv;
771 vert = new warp_Vertex(pos, tur, tvr);
772 }
773 else
774 {
775 tu *= scaleu;
776 tu += offsetu;
777 tv *= scalev;
778 tv += offsetv;
779 vert = new warp_Vertex(pos, tu, tv);
780 }
781
782 faceObj.addVertex(vert);
783 }
784 }
785
786 for (int j = 0; j < face.Indices.Count; j += 3)
787 {
788 faceObj.addTriangle(
789 face.Indices[j + 0],
790 face.Indices[j + 1],
791 face.Indices[j + 2]);
792 }
793
794 faceObj.scaleSelf(prim.Scale.X, prim.Scale.Z, prim.Scale.Y);
795 faceObj.transform(m);
796 faceObj.setPos(primPos);
797
798 renderer.Scene.addObject(meshName, faceObj);
799 renderer.SetObjectMaterial(meshName, materialName);
800 }
801 }
802
803 private int GetFaceColor(Primitive.TextureEntryFace face)
804 {
805 int color;
806 Color4 ctmp = Color4.White;
807
808 if (face.TextureID == UUID.Zero)
809 return warp_Color.White;
810
811 if (!m_colors.TryGetValue(face.TextureID, out color))
812 {
813 bool fetched = false;
814
815 // Attempt to fetch the texture metadata
816 string cacheName = "MAPCLR" + face.TextureID.ToString();
817 AssetBase metadata = m_scene.AssetService.GetCached(cacheName);
818 if (metadata != null)
819 {
820 OSDMap map = null;
821 try { map = OSDParser.Deserialize(metadata.Data) as OSDMap; } catch { }
822
823 if (map != null)
824 {
825 ctmp = map["X-RGBA"].AsColor4();
826 fetched = true;
827 }
828 }
829
830 if (!fetched)
831 {
832 // Fetch the texture, decode and get the average color,
833 // then save it to a temporary metadata asset
834 AssetBase textureAsset = m_scene.AssetService.Get(face.TextureID.ToString());
835 if (textureAsset != null)
836 {
837 int width, height;
838 ctmp = GetAverageColor(textureAsset.FullID, textureAsset.Data, out width, out height);
839
840 OSDMap data = new OSDMap { { "X-RGBA", OSD.FromColor4(ctmp) } };
841 metadata = new AssetBase
842 {
843 Data = System.Text.Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(data)),
844 Description = "Metadata for texture color" + face.TextureID.ToString(),
845 Flags = AssetFlags.Collectable,
846 FullID = UUID.Zero,
847 ID = cacheName,
848 Local = true,
849 Temporary = true,
850 Name = String.Empty,
851 Type = (sbyte)AssetType.Unknown
852 };
853 m_scene.AssetService.Store(metadata);
854 }
855 else
856 {
857 ctmp = new Color4(0.5f, 0.5f, 0.5f, 1.0f);
858 }
859 }
860 color = ConvertColor(ctmp);
861 m_colors[face.TextureID] = color;
862 }
863
864 return color;
865 }
866
867 private string GetOrCreateMaterial(WarpRenderer renderer, Color4 color)
868 {
869 string name = color.ToString();
870
871 warp_Material material = renderer.Scene.material(name);
872 if (material != null)
873 return name;
874
875 renderer.AddMaterial(name, ConvertColor(color));
876 return name;
877 }
878
879 public string GetOrCreateMaterial(WarpRenderer renderer, Color4 faceColor, UUID textureID, bool useAverageTextureColor, SceneObjectPart sop)
880 {
881 int color = ConvertColor(faceColor);
882 string idstr = textureID.ToString() + color.ToString();
883 string materialName = "MAPMAT" + idstr;
884
885 if (renderer.Scene.material(materialName) != null)
886 return materialName;
887
888 warp_Material mat = new warp_Material();
889 warp_Texture texture = GetTexture(textureID, sop);
890 if (texture != null)
891 {
892 if (useAverageTextureColor)
893 color = warp_Color.multiply(color, texture.averageColor);
894 else
895 mat.setTexture(texture);
896 }
897 else
898 color = warp_Color.multiply(color, warp_Color.Grey);
899
900 mat.setColor(color);
901 renderer.Scene.addMaterial(materialName, mat);
902
903 return materialName;
904 }
905
906 private warp_Texture GetTexture(UUID id, SceneObjectPart sop)
907 {
908 warp_Texture ret = null;
909 if (id == UUID.Zero)
910 return ret;
911
912 if (m_warpTextures.TryGetValue(id.ToString(), out ret))
913 return ret;
914
915 byte[] asset = m_scene.AssetService.GetData(id.ToString());
916
917 if (asset != null)
918 {
919 try
920 {
921 using (Bitmap img = (Bitmap)m_imgDecoder.DecodeToImage(asset))
922 ret = new warp_Texture(img, 8); // reduce textures size to 256x256
923 }
924 catch (Exception e)
925 {
926 m_log.WarnFormat("[Warp3D]: Failed to decode texture {0} for prim {1} at {2}, exception {3}", id.ToString(), sop.Name, sop.GetWorldPosition().ToString(), e.Message);
927 }
928 }
929 else
930 m_log.WarnFormat("[Warp3D]: missing texture {0} data for prim {1} at {2}",
931 id.ToString(), sop.Name, sop.GetWorldPosition().ToString());
932
933 m_warpTextures[id.ToString()] = ret;
934 return ret;
935 }
936
937 #endregion Rendering Methods
938
939 #region Static Helpers
940 // Note: axis change.
941 private static warp_Vector ConvertVector(float x, float y, float z)
942 {
943 return new warp_Vector(x, z, y);
944 }
945
946 private static warp_Vector ConvertVector(Vector3 vector)
947 {
948 return new warp_Vector(vector.X, vector.Z, vector.Y);
949 }
950
951 private static warp_Quaternion ConvertQuaternion(Quaternion quat)
952 {
953 return new warp_Quaternion(quat.X, quat.Z, quat.Y, -quat.W);
954 }
955
956 private static int ConvertColor(Color4 color)
957 {
958 int c = warp_Color.getColor((byte)(color.R * 255f), (byte)(color.G * 255f), (byte)(color.B * 255f), (byte)(color.A * 255f));
959 return c;
960 }
961
962 private static Vector3 SurfaceNormal(Vector3 c1, Vector3 c2, Vector3 c3)
963 {
964 Vector3 edge1 = new Vector3(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
965 Vector3 edge2 = new Vector3(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
966
967 Vector3 normal = Vector3.Cross(edge1, edge2);
968 normal.Normalize();
969
970 return normal;
971 }
972
973 public Color4 GetAverageColor(UUID textureID, byte[] j2kData, out int width, out int height)
974 {
975 ulong r = 0;
976 ulong g = 0;
977 ulong b = 0;
978 ulong a = 0;
979 int pixelBytes;
980
981 try
982 {
983 using (MemoryStream stream = new MemoryStream(j2kData))
984 using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream))
985 {
986 width = bitmap.Width;
987 height = bitmap.Height;
988
989 BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
990 pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;
991
992 // Sum up the individual channels
993 unsafe
994 {
995 if (pixelBytes == 4)
996 {
997 for (int y = 0; y < height; y++)
998 {
999 byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
1000
1001 for (int x = 0; x < width; x++)
1002 {
1003 b += row[x * pixelBytes + 0];
1004 g += row[x * pixelBytes + 1];
1005 r += row[x * pixelBytes + 2];
1006 a += row[x * pixelBytes + 3];
1007 }
1008 }
1009 }
1010 else
1011 {
1012 for (int y = 0; y < height; y++)
1013 {
1014 byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
1015
1016 for (int x = 0; x < width; x++)
1017 {
1018 b += row[x * pixelBytes + 0];
1019 g += row[x * pixelBytes + 1];
1020 r += row[x * pixelBytes + 2];
1021 }
1022 }
1023 }
1024 }
1025 }
1026 // Get the averages for each channel
1027 const decimal OO_255 = 1m / 255m;
1028 decimal totalPixels = (decimal)(width * height);
1029
1030 decimal rm = ((decimal)r / totalPixels) * OO_255;
1031 decimal gm = ((decimal)g / totalPixels) * OO_255;
1032 decimal bm = ((decimal)b / totalPixels) * OO_255;
1033 decimal am = ((decimal)a / totalPixels) * OO_255;
1034
1035 if (pixelBytes == 3)
1036 am = 1m;
1037
1038 return new Color4((float)rm, (float)gm, (float)bm, (float)am);
1039
1040 }
1041 catch (Exception ex)
1042 {
1043 m_log.WarnFormat(
1044 "[WARP 3D IMAGE MODULE]: Error decoding JPEG2000 texture {0} ({1} bytes): {2}",
1045 textureID, j2kData.Length, ex.Message);
1046
1047 width = 0;
1048 height = 0;
1049 return new Color4(0.5f, 0.5f, 0.5f, 1.0f);
1050 }
1051 }
1052
1053 #endregion Static Helpers
1054 }
1055
1056 public static class ImageUtils
1057 {
1058 /// <summary>
1059 /// Performs bilinear interpolation between four values
1060 /// </summary>
1061 /// <param name="v00">First, or top left value</param>
1062 /// <param name="v01">Second, or top right value</param>
1063 /// <param name="v10">Third, or bottom left value</param>
1064 /// <param name="v11">Fourth, or bottom right value</param>
1065 /// <param name="xPercent">Interpolation value on the X axis, between 0.0 and 1.0</param>
1066 /// <param name="yPercent">Interpolation value on fht Y axis, between 0.0 and 1.0</param>
1067 /// <returns>The bilinearly interpolated result</returns>
1068 public static float Bilinear(float v00, float v01, float v10, float v11, float xPercent, float yPercent)
1069 {
1070 return Utils.Lerp(Utils.Lerp(v00, v01, xPercent), Utils.Lerp(v10, v11, xPercent), yPercent);
1071 }
1072 }
1073}