opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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  
28 using System;
29 using System.Collections.Generic;
30 using System.Drawing;
31 using System.Drawing.Imaging;
32 using System.IO;
33 using System.Reflection;
34  
35 using CSJ2K;
36 using Nini.Config;
37 using log4net;
38 using Rednettle.Warp3D;
39 using Mono.Addins;
40  
41 using OpenSim.Framework;
42 using OpenSim.Region.Framework.Interfaces;
43 using OpenSim.Region.Framework.Scenes;
44 using OpenSim.Region.Physics.Manager;
45 using OpenSim.Services.Interfaces;
46  
47 using OpenMetaverse;
48 using OpenMetaverse.Assets;
49 using OpenMetaverse.Imaging;
50 using OpenMetaverse.Rendering;
51 using OpenMetaverse.StructuredData;
52  
53 using WarpRenderer = global::Warp3D.Warp3D;
54  
55 namespace OpenSim.Region.CoreModules.World.Warp3DMap
56 {
57 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "Warp3DImageModule")]
58 public class Warp3DImageModule : IMapImageGenerator, INonSharedRegionModule
59 {
60 private static readonly UUID TEXTURE_METADATA_MAGIC = new UUID("802dc0e0-f080-4931-8b57-d1be8611c4f3");
61 private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 216);
62  
63 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
64 private static string LogHeader = "[WARP 3D IMAGE MODULE]";
65  
66 private Scene m_scene;
67 private IRendering m_primMesher;
68 private Dictionary<UUID, Color4> m_colors = new Dictionary<UUID, Color4>();
69  
70 private IConfigSource m_config;
71 private bool m_drawPrimVolume = true; // true if should render the prims on the tile
72 private bool m_textureTerrain = true; // true if to create terrain splatting texture
73 private bool m_texturePrims = true; // true if should texture the rendered prims
74 private float m_texturePrimSize = 48f; // size of prim before we consider texturing it
75 private bool m_renderMeshes = false; // true if to render meshes rather than just bounding boxes
76 private bool m_useAntiAliasing = false; // true if to anti-alias the rendered image
77  
78 private bool m_Enabled = false;
79  
80 #region Region Module interface
81  
82 public void Initialise(IConfigSource source)
83 {
84 m_config = source;
85  
86 string[] configSections = new string[] { "Map", "Startup" };
87  
88 if (Util.GetConfigVarFromSections<string>(
89 m_config, "MapImageModule", configSections, "MapImageModule") != "Warp3DImageModule")
90 return;
91  
92 m_Enabled = true;
93  
94 m_drawPrimVolume
95 = Util.GetConfigVarFromSections<bool>(m_config, "DrawPrimOnMapTile", configSections, m_drawPrimVolume);
96 m_textureTerrain
97 = Util.GetConfigVarFromSections<bool>(m_config, "TextureOnMapTile", configSections, m_textureTerrain);
98 m_texturePrims
99 = Util.GetConfigVarFromSections<bool>(m_config, "TexturePrims", configSections, m_texturePrims);
100 m_texturePrimSize
101 = Util.GetConfigVarFromSections<float>(m_config, "TexturePrimSize", configSections, m_texturePrimSize);
102 m_renderMeshes
103 = Util.GetConfigVarFromSections<bool>(m_config, "RenderMeshes", configSections, m_renderMeshes);
104 m_useAntiAliasing
105 = Util.GetConfigVarFromSections<bool>(m_config, "UseAntiAliasing", configSections, m_useAntiAliasing);
106  
107 }
108  
109 public void AddRegion(Scene scene)
110 {
111 if (!m_Enabled)
112 return;
113  
114 m_scene = scene;
115  
116 List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
117 if (renderers.Count > 0)
118 {
119 m_primMesher = RenderingLoader.LoadRenderer(renderers[0]);
120 m_log.DebugFormat("[WARP 3D IMAGE MODULE]: Loaded prim mesher {0}", m_primMesher);
121 }
122 else
123 {
124 m_log.Debug("[WARP 3D IMAGE MODULE]: No prim mesher loaded, prim rendering will be disabled");
125 }
126  
127 m_scene.RegisterModuleInterface<IMapImageGenerator>(this);
128 }
129  
130 public void RegionLoaded(Scene scene)
131 {
132 }
133  
134 public void RemoveRegion(Scene scene)
135 {
136 }
137  
138 public void Close()
139 {
140 }
141  
142 public string Name
143 {
144 get { return "Warp3DImageModule"; }
145 }
146  
147 public Type ReplaceableInterface
148 {
149 get { return null; }
150 }
151  
152 #endregion
153  
154 #region IMapImageGenerator Members
155  
156 public Bitmap CreateMapTile()
157 {
158 // Vector3 camPos = new Vector3(127.5f, 127.5f, 221.7025033688163f);
159 // Camera above the middle of the region
160 Vector3 camPos = new Vector3(
161 m_scene.RegionInfo.RegionSizeX/2 - 0.5f,
162 m_scene.RegionInfo.RegionSizeY/2 - 0.5f,
163 221.7025033688163f);
164 // Viewport viewing down onto the region
165 Viewport viewport = new Viewport(camPos, -Vector3.UnitZ, 1024f, 0.1f,
166 (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY,
167 (float)m_scene.RegionInfo.RegionSizeX, (float)m_scene.RegionInfo.RegionSizeY );
168 // Fill the viewport and return the image
169 return CreateMapTile(viewport, false);
170 }
171  
172 public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float fov, int width, int height, bool useTextures)
173 {
174 Viewport viewport = new Viewport(camPos, camDir, fov, Constants.RegionSize, 0.1f, width, height);
175 return CreateMapTile(viewport, useTextures);
176 }
177  
178 public Bitmap CreateMapTile(Viewport viewport, bool useTextures)
179 {
180 m_colors.Clear();
181  
182 int width = viewport.Width;
183 int height = viewport.Height;
184  
185 if (m_useAntiAliasing)
186 {
187 width *= 2;
188 height *= 2;
189 }
190  
191 WarpRenderer renderer = new WarpRenderer();
192 renderer.CreateScene(width, height);
193 renderer.Scene.autoCalcNormals = false;
194  
195 #region Camera
196  
197 warp_Vector pos = ConvertVector(viewport.Position);
198 pos.z -= 0.001f; // Works around an issue with the Warp3D camera
199 warp_Vector lookat = warp_Vector.add(ConvertVector(viewport.Position), ConvertVector(viewport.LookDirection));
200  
201 renderer.Scene.defaultCamera.setPos(pos);
202 renderer.Scene.defaultCamera.lookAt(lookat);
203  
204 if (viewport.Orthographic)
205 {
206 renderer.Scene.defaultCamera.isOrthographic = true;
207 renderer.Scene.defaultCamera.orthoViewWidth = viewport.OrthoWindowWidth;
208 renderer.Scene.defaultCamera.orthoViewHeight = viewport.OrthoWindowHeight;
209 }
210 else
211 {
212 float fov = viewport.FieldOfView;
213 fov *= 1.75f; // FIXME: ???
214 renderer.Scene.defaultCamera.setFov(fov);
215 }
216  
217 #endregion Camera
218  
219 renderer.Scene.addLight("Light1", new warp_Light(new warp_Vector(1.0f, 0.5f, 1f), 0xffffff, 0, 320, 40));
220 renderer.Scene.addLight("Light2", new warp_Light(new warp_Vector(-1f, -1f, 1f), 0xffffff, 0, 100, 40));
221  
222 CreateWater(renderer);
223 CreateTerrain(renderer, m_textureTerrain);
224 if (m_drawPrimVolume)
225 CreateAllPrims(renderer, useTextures);
226  
227 renderer.Render();
228 Bitmap bitmap = renderer.Scene.getImage();
229  
230 if (m_useAntiAliasing)
231 {
232 using (Bitmap origBitmap = bitmap)
233 bitmap = ImageUtils.ResizeImage(origBitmap, viewport.Width, viewport.Height);
234 }
235  
236 // XXX: It shouldn't really be necesary to force a GC here as one should occur anyway pretty shortly
237 // afterwards. It's generally regarded as a bad idea to manually GC. If Warp3D is using lots of memory
238 // then this may be some issue with the Warp3D code itself, though it's also quite possible that generating
239 // this map tile simply takes a lot of memory.
240 foreach (var o in renderer.Scene.objectData.Values)
241 {
242 warp_Object obj = (warp_Object)o;
243 obj.vertexData = null;
244 obj.triangleData = null;
245 }
246 renderer.Scene.removeAllObjects();
247 renderer = null;
248 viewport = null;
249 m_colors.Clear();
250 GC.Collect();
251 m_log.Debug("[WARP 3D IMAGE MODULE]: GC.Collect()");
252  
253 return bitmap;
254 }
255  
256 public byte[] WriteJpeg2000Image()
257 {
258 try
259 {
260 using (Bitmap mapbmp = CreateMapTile())
261 return OpenJPEG.EncodeFromImage(mapbmp, true);
262 }
263 catch (Exception e)
264 {
265 // JPEG2000 encoder failed
266 m_log.Error("[WARP 3D IMAGE MODULE]: Failed generating terrain map: ", e);
267 }
268  
269 return null;
270 }
271  
272 #endregion
273  
274 #region Rendering Methods
275  
276 // Add a water plane to the renderer.
277 private void CreateWater(WarpRenderer renderer)
278 {
279 float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
280  
281 renderer.AddPlane("Water", m_scene.RegionInfo.RegionSizeX * 0.5f);
282 renderer.Scene.sceneobject("Water").setPos(m_scene.RegionInfo.RegionSizeX/2 - 0.5f,
283 waterHeight,
284 m_scene.RegionInfo.RegionSizeY/2 - 0.5f );
285  
286 warp_Material waterColorMaterial = new warp_Material(ConvertColor(WATER_COLOR));
287 waterColorMaterial.setReflectivity(0); // match water color with standard map module thanks lkalif
288 waterColorMaterial.setTransparency((byte)((1f - WATER_COLOR.A) * 255f));
289 renderer.Scene.addMaterial("WaterColor", waterColorMaterial);
290 renderer.SetObjectMaterial("Water", "WaterColor");
291 }
292  
293 // Add a terrain to the renderer.
294 // Note that we create a 'low resolution' 256x256 vertex terrain rather than trying for
295 // full resolution. This saves a lot of memory especially for very large regions.
296 private void CreateTerrain(WarpRenderer renderer, bool textureTerrain)
297 {
298 ITerrainChannel terrain = m_scene.Heightmap;
299  
300 // 'diff' is the difference in scale between the real region size and the size of terrain we're buiding
301 float diff = (float)m_scene.RegionInfo.RegionSizeX / 256f;
302  
303 warp_Object obj = new warp_Object(256 * 256, 255 * 255 * 2);
304  
305 // Create all the vertices for the terrain
306 for (float y = 0; y < m_scene.RegionInfo.RegionSizeY; y += diff)
307 {
308 for (float x = 0; x < m_scene.RegionInfo.RegionSizeX; x += diff)
309 {
310 warp_Vector pos = ConvertVector(x, y, (float)terrain[(int)x, (int)y]);
311 obj.addVertex(new warp_Vertex(pos,
312 x / (float)m_scene.RegionInfo.RegionSizeX,
313 (((float)m_scene.RegionInfo.RegionSizeY) - y) / m_scene.RegionInfo.RegionSizeY) );
314 }
315 }
316  
317 // Now that we have all the vertices, make another pass and create
318 // the normals for each of the surface triangles and
319 // create the list of triangle indices.
320 for (float y = 0; y < m_scene.RegionInfo.RegionSizeY; y += diff)
321 {
322 for (float x = 0; x < m_scene.RegionInfo.RegionSizeX; x += diff)
323 {
324 float newX = x / diff;
325 float newY = y / diff;
326 if (newX < 255 && newY < 255)
327 {
328 int v = (int)newY * 256 + (int)newX;
329  
330 // Normal for a triangle made up of three adjacent vertices
331 Vector3 v1 = new Vector3(newX, newY, (float)terrain[(int)x, (int)y]);
332 Vector3 v2 = new Vector3(newX + 1, newY, (float)terrain[(int)(x + 1), (int)y]);
333 Vector3 v3 = new Vector3(newX, newY + 1, (float)terrain[(int)x, ((int)(y + 1))]);
334 warp_Vector norm = ConvertVector(SurfaceNormal(v1, v2, v3));
335 norm = norm.reverse();
336 obj.vertex(v).n = norm;
337  
338 // Make two triangles for each of the squares in the grid of vertices
339 obj.addTriangle(
340 v,
341 v + 1,
342 v + 256);
343  
344 obj.addTriangle(
345 v + 256 + 1,
346 v + 256,
347 v + 1);
348 }
349 }
350 }
351  
352 renderer.Scene.addObject("Terrain", obj);
353  
354 UUID[] textureIDs = new UUID[4];
355 float[] startHeights = new float[4];
356 float[] heightRanges = new float[4];
357  
358 OpenSim.Framework.RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings;
359  
360 textureIDs[0] = regionInfo.TerrainTexture1;
361 textureIDs[1] = regionInfo.TerrainTexture2;
362 textureIDs[2] = regionInfo.TerrainTexture3;
363 textureIDs[3] = regionInfo.TerrainTexture4;
364  
365 startHeights[0] = (float)regionInfo.Elevation1SW;
366 startHeights[1] = (float)regionInfo.Elevation1NW;
367 startHeights[2] = (float)regionInfo.Elevation1SE;
368 startHeights[3] = (float)regionInfo.Elevation1NE;
369  
370 heightRanges[0] = (float)regionInfo.Elevation2SW;
371 heightRanges[1] = (float)regionInfo.Elevation2NW;
372 heightRanges[2] = (float)regionInfo.Elevation2SE;
373 heightRanges[3] = (float)regionInfo.Elevation2NE;
374  
375 uint globalX, globalY;
376 Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out globalX, out globalY);
377  
378 warp_Texture texture;
379 using (
380 Bitmap image
381 = TerrainSplat.Splat(terrain, textureIDs, startHeights, heightRanges,
382 new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain))
383 {
384 texture = new warp_Texture(image);
385 }
386  
387 warp_Material material = new warp_Material(texture);
388 material.setReflectivity(50);
389 renderer.Scene.addMaterial("TerrainColor", material);
390 renderer.Scene.material("TerrainColor").setReflectivity(0); // reduces tile seams a bit thanks lkalif
391 renderer.SetObjectMaterial("Terrain", "TerrainColor");
392 }
393  
394 private void CreateAllPrims(WarpRenderer renderer, bool useTextures)
395 {
396 if (m_primMesher == null)
397 return;
398  
399 m_scene.ForEachSOG(
400 delegate(SceneObjectGroup group)
401 {
402 CreatePrim(renderer, group.RootPart, useTextures);
403 foreach (SceneObjectPart child in group.Parts)
404 CreatePrim(renderer, child, useTextures);
405 }
406 );
407 }
408  
409 private void CreatePrim(WarpRenderer renderer, SceneObjectPart prim,
410 bool useTextures)
411 {
412 const float MIN_SIZE = 2f;
413  
414 if ((PCode)prim.Shape.PCode != PCode.Prim)
415 return;
416 if (prim.Scale.LengthSquared() < MIN_SIZE * MIN_SIZE)
417 return;
418  
419 FacetedMesh renderMesh = null;
420 Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset);
421  
422 if (m_renderMeshes)
423 {
424 if (omvPrim.Sculpt != null && omvPrim.Sculpt.SculptTexture != UUID.Zero)
425 {
426 // Try fetchinng the asset
427 byte[] sculptAsset = m_scene.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString());
428 if (sculptAsset != null)
429 {
430 // Is it a mesh?
431 if (omvPrim.Sculpt.Type == SculptType.Mesh)
432 {
433 AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset);
434 FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, DetailLevel.Highest, out renderMesh);
435 meshAsset = null;
436 }
437 else // It's sculptie
438 {
439 IJ2KDecoder imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
440 if (imgDecoder != null)
441 {
442 Image sculpt = imgDecoder.DecodeToImage(sculptAsset);
443 if (sculpt != null)
444 {
445 renderMesh = m_primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt,
446 DetailLevel.Medium);
447 sculpt.Dispose();
448 }
449 }
450 }
451 }
452 }
453 }
454  
455 // If not a mesh or sculptie, try the regular mesher
456 if (renderMesh == null)
457 {
458 renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium);
459 }
460  
461 if (renderMesh == null)
462 return;
463  
464 warp_Vector primPos = ConvertVector(prim.GetWorldPosition());
465 warp_Quaternion primRot = ConvertQuaternion(prim.RotationOffset);
466  
467 warp_Matrix m = warp_Matrix.quaternionMatrix(primRot);
468  
469 if (prim.ParentID != 0)
470 {
471 SceneObjectGroup group = m_scene.SceneGraph.GetGroupByPrim(prim.LocalId);
472 if (group != null)
473 m.transform(warp_Matrix.quaternionMatrix(ConvertQuaternion(group.RootPart.RotationOffset)));
474 }
475  
476 warp_Vector primScale = ConvertVector(prim.Scale);
477  
478 string primID = prim.UUID.ToString();
479  
480 // Create the prim faces
481 // TODO: Implement the useTextures flag behavior
482 for (int i = 0; i < renderMesh.Faces.Count; i++)
483 {
484 Face face = renderMesh.Faces[i];
485 string meshName = primID + "-Face-" + i.ToString();
486  
487 // Avoid adding duplicate meshes to the scene
488 if (renderer.Scene.objectData.ContainsKey(meshName))
489 {
490 continue;
491 }
492  
493 warp_Object faceObj = new warp_Object(face.Vertices.Count, face.Indices.Count / 3);
494  
495 for (int j = 0; j < face.Vertices.Count; j++)
496 {
497 Vertex v = face.Vertices[j];
498  
499 warp_Vector pos = ConvertVector(v.Position);
500 warp_Vector norm = ConvertVector(v.Normal);
501  
502 if (prim.Shape.SculptTexture == UUID.Zero)
503 norm = norm.reverse();
504 warp_Vertex vert = new warp_Vertex(pos, norm, v.TexCoord.X, v.TexCoord.Y);
505  
506 faceObj.addVertex(vert);
507 }
508  
509 for (int j = 0; j < face.Indices.Count; j += 3)
510 {
511 faceObj.addTriangle(
512 face.Indices[j + 0],
513 face.Indices[j + 1],
514 face.Indices[j + 2]);
515 }
516  
517 Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i);
518 Color4 faceColor = GetFaceColor(teFace);
519 string materialName = String.Empty;
520 if (m_texturePrims && prim.Scale.LengthSquared() > m_texturePrimSize*m_texturePrimSize)
521 materialName = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID);
522 else
523 materialName = GetOrCreateMaterial(renderer, faceColor);
524  
525 faceObj.transform(m);
526 faceObj.setPos(primPos);
527 faceObj.scaleSelf(primScale.x, primScale.y, primScale.z);
528  
529 renderer.Scene.addObject(meshName, faceObj);
530  
531 renderer.SetObjectMaterial(meshName, materialName);
532 }
533 }
534  
535 private Color4 GetFaceColor(Primitive.TextureEntryFace face)
536 {
537 Color4 color;
538  
539 if (face.TextureID == UUID.Zero)
540 return face.RGBA;
541  
542 if (!m_colors.TryGetValue(face.TextureID, out color))
543 {
544 bool fetched = false;
545  
546 // Attempt to fetch the texture metadata
547 UUID metadataID = UUID.Combine(face.TextureID, TEXTURE_METADATA_MAGIC);
548 AssetBase metadata = m_scene.AssetService.GetCached(metadataID.ToString());
549 if (metadata != null)
550 {
551 OSDMap map = null;
552 try { map = OSDParser.Deserialize(metadata.Data) as OSDMap; } catch { }
553  
554 if (map != null)
555 {
556 color = map["X-JPEG2000-RGBA"].AsColor4();
557 fetched = true;
558 }
559 }
560  
561 if (!fetched)
562 {
563 // Fetch the texture, decode and get the average color,
564 // then save it to a temporary metadata asset
565 AssetBase textureAsset = m_scene.AssetService.Get(face.TextureID.ToString());
566 if (textureAsset != null)
567 {
568 int width, height;
569 color = GetAverageColor(textureAsset.FullID, textureAsset.Data, out width, out height);
570  
571 OSDMap data = new OSDMap { { "X-JPEG2000-RGBA", OSD.FromColor4(color) } };
572 metadata = new AssetBase
573 {
574 Data = System.Text.Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(data)),
575 Description = "Metadata for JPEG2000 texture " + face.TextureID.ToString(),
576 Flags = AssetFlags.Collectable,
577 FullID = metadataID,
578 ID = metadataID.ToString(),
579 Local = true,
580 Temporary = true,
581 Name = String.Empty,
582 Type = (sbyte)AssetType.Unknown
583 };
584 m_scene.AssetService.Store(metadata);
585 }
586 else
587 {
588 color = new Color4(0.5f, 0.5f, 0.5f, 1.0f);
589 }
590 }
591  
592 m_colors[face.TextureID] = color;
593 }
594  
595 return color * face.RGBA;
596 }
597  
598 private string GetOrCreateMaterial(WarpRenderer renderer, Color4 color)
599 {
600 string name = color.ToString();
601  
602 warp_Material material = renderer.Scene.material(name);
603 if (material != null)
604 return name;
605  
606 renderer.AddMaterial(name, ConvertColor(color));
607 if (color.A < 1f)
608 renderer.Scene.material(name).setTransparency((byte)((1f - color.A) * 255f));
609 return name;
610 }
611  
612 public string GetOrCreateMaterial(WarpRenderer renderer, Color4 faceColor, UUID textureID)
613 {
614 string materialName = "Color-" + faceColor.ToString() + "-Texture-" + textureID.ToString();
615  
616 if (renderer.Scene.material(materialName) == null)
617 {
618 renderer.AddMaterial(materialName, ConvertColor(faceColor));
619 if (faceColor.A < 1f)
620 {
621 renderer.Scene.material(materialName).setTransparency((byte) ((1f - faceColor.A)*255f));
622 }
623 warp_Texture texture = GetTexture(textureID);
624 if (texture != null)
625 renderer.Scene.material(materialName).setTexture(texture);
626 }
627  
628 return materialName;
629 }
630  
631 private warp_Texture GetTexture(UUID id)
632 {
633 warp_Texture ret = null;
634 byte[] asset = m_scene.AssetService.GetData(id.ToString());
635 if (asset != null)
636 {
637 IJ2KDecoder imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
638 Bitmap img = (Bitmap) imgDecoder.DecodeToImage(asset);
639 if (img != null)
640 {
641 return new warp_Texture(img);
642 }
643 }
644 return ret;
645 }
646  
647 #endregion Rendering Methods
648  
649 #region Static Helpers
650  
651 // Note: axis change.
652 private static warp_Vector ConvertVector(float x, float y, float z)
653 {
654 return new warp_Vector(x, z, y);
655 }
656  
657 private static warp_Vector ConvertVector(Vector3 vector)
658 {
659 return new warp_Vector(vector.X, vector.Z, vector.Y);
660 }
661  
662 private static warp_Quaternion ConvertQuaternion(Quaternion quat)
663 {
664 return new warp_Quaternion(quat.X, quat.Z, quat.Y, -quat.W);
665 }
666  
667 private static int ConvertColor(Color4 color)
668 {
669 int c = warp_Color.getColor((byte)(color.R * 255f), (byte)(color.G * 255f), (byte)(color.B * 255f));
670 if (color.A < 1f)
671 c |= (byte)(color.A * 255f) << 24;
672  
673 return c;
674 }
675  
676 private static Vector3 SurfaceNormal(Vector3 c1, Vector3 c2, Vector3 c3)
677 {
678 Vector3 edge1 = new Vector3(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
679 Vector3 edge2 = new Vector3(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
680  
681 Vector3 normal = Vector3.Cross(edge1, edge2);
682 normal.Normalize();
683  
684 return normal;
685 }
686  
687 public static Color4 GetAverageColor(UUID textureID, byte[] j2kData, out int width, out int height)
688 {
689 ulong r = 0;
690 ulong g = 0;
691 ulong b = 0;
692 ulong a = 0;
693  
694 using (MemoryStream stream = new MemoryStream(j2kData))
695 {
696 try
697 {
698 int pixelBytes;
699  
700 using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream))
701 {
702 width = bitmap.Width;
703 height = bitmap.Height;
704  
705 BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
706 pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;
707  
708 // Sum up the individual channels
709 unsafe
710 {
711 if (pixelBytes == 4)
712 {
713 for (int y = 0; y < height; y++)
714 {
715 byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
716  
717 for (int x = 0; x < width; x++)
718 {
719 b += row[x * pixelBytes + 0];
720 g += row[x * pixelBytes + 1];
721 r += row[x * pixelBytes + 2];
722 a += row[x * pixelBytes + 3];
723 }
724 }
725 }
726 else
727 {
728 for (int y = 0; y < height; y++)
729 {
730 byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
731  
732 for (int x = 0; x < width; x++)
733 {
734 b += row[x * pixelBytes + 0];
735 g += row[x * pixelBytes + 1];
736 r += row[x * pixelBytes + 2];
737 }
738 }
739 }
740 }
741 }
742  
743 // Get the averages for each channel
744 const decimal OO_255 = 1m / 255m;
745 decimal totalPixels = (decimal)(width * height);
746  
747 decimal rm = ((decimal)r / totalPixels) * OO_255;
748 decimal gm = ((decimal)g / totalPixels) * OO_255;
749 decimal bm = ((decimal)b / totalPixels) * OO_255;
750 decimal am = ((decimal)a / totalPixels) * OO_255;
751  
752 if (pixelBytes == 3)
753 am = 1m;
754  
755 return new Color4((float)rm, (float)gm, (float)bm, (float)am);
756 }
757 catch (Exception ex)
758 {
759 m_log.WarnFormat(
760 "[WARP 3D IMAGE MODULE]: Error decoding JPEG2000 texture {0} ({1} bytes): {2}",
761 textureID, j2kData.Length, ex.Message);
762  
763 width = 0;
764 height = 0;
765 return new Color4(0.5f, 0.5f, 0.5f, 1.0f);
766 }
767 }
768 }
769  
770 #endregion Static Helpers
771 }
772  
773 public static class ImageUtils
774 {
775 /// <summary>
776 /// Performs bilinear interpolation between four values
777 /// </summary>
778 /// <param name="v00">First, or top left value</param>
779 /// <param name="v01">Second, or top right value</param>
780 /// <param name="v10">Third, or bottom left value</param>
781 /// <param name="v11">Fourth, or bottom right value</param>
782 /// <param name="xPercent">Interpolation value on the X axis, between 0.0 and 1.0</param>
783 /// <param name="yPercent">Interpolation value on fht Y axis, between 0.0 and 1.0</param>
784 /// <returns>The bilinearly interpolated result</returns>
785 public static float Bilinear(float v00, float v01, float v10, float v11, float xPercent, float yPercent)
786 {
787 return Utils.Lerp(Utils.Lerp(v00, v01, xPercent), Utils.Lerp(v10, v11, xPercent), yPercent);
788 }
789  
790 /// <summary>
791 /// Performs a high quality image resize
792 /// </summary>
793 /// <param name="image">Image to resize</param>
794 /// <param name="width">New width</param>
795 /// <param name="height">New height</param>
796 /// <returns>Resized image</returns>
797 public static Bitmap ResizeImage(Image image, int width, int height)
798 {
799 Bitmap result = new Bitmap(width, height);
800  
801 using (Graphics graphics = Graphics.FromImage(result))
802 {
803 graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
804 graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
805 graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
806 graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
807  
808 graphics.DrawImage(image, 0, 0, result.Width, result.Height);
809 }
810  
811 return result;
812 }
813 }
814 }