diff options
author | Justin Clark-Casey (justincc) | 2012-06-06 04:15:00 +0100 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2012-06-06 04:15:00 +0100 |
commit | 53c25a47780b5b1167f8e574dc8e9b935a0d5ddd (patch) | |
tree | c4cb95f3f4626fcbf9e3c792f89191d94071e445 /OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs | |
parent | Actively dispose of Bitmaps in Warp3D image module and world map module once ... (diff) | |
download | opensim-SC_OLD-53c25a47780b5b1167f8e574dc8e9b935a0d5ddd.zip opensim-SC_OLD-53c25a47780b5b1167f8e574dc8e9b935a0d5ddd.tar.gz opensim-SC_OLD-53c25a47780b5b1167f8e574dc8e9b935a0d5ddd.tar.bz2 opensim-SC_OLD-53c25a47780b5b1167f8e574dc8e9b935a0d5ddd.tar.xz |
Rename MapImageModule for Warp3D to Warp3DImageModule to match its class name and make it easier to distinguish between map image modules.
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs new file mode 100644 index 0000000..3538b46 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs | |||
@@ -0,0 +1,674 @@ | |||
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 | using CSJ2K; | ||
35 | using Nini.Config; | ||
36 | using log4net; | ||
37 | using Rednettle.Warp3D; | ||
38 | using OpenMetaverse; | ||
39 | using OpenMetaverse.Imaging; | ||
40 | using OpenMetaverse.Rendering; | ||
41 | using OpenMetaverse.StructuredData; | ||
42 | using OpenSim.Framework; | ||
43 | using OpenSim.Region.Framework.Interfaces; | ||
44 | using OpenSim.Region.Framework.Scenes; | ||
45 | using OpenSim.Region.Physics.Manager; | ||
46 | using OpenSim.Services.Interfaces; | ||
47 | |||
48 | using WarpRenderer = global::Warp3D.Warp3D; | ||
49 | |||
50 | namespace OpenSim.Region.CoreModules.World.Warp3DMap | ||
51 | { | ||
52 | public class Warp3DImageModule : IMapImageGenerator, INonSharedRegionModule | ||
53 | { | ||
54 | private static readonly UUID TEXTURE_METADATA_MAGIC = new UUID("802dc0e0-f080-4931-8b57-d1be8611c4f3"); | ||
55 | private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 216); | ||
56 | |||
57 | private static readonly ILog m_log = | ||
58 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
59 | |||
60 | private Scene m_scene; | ||
61 | private IRendering m_primMesher; | ||
62 | private IConfigSource m_config; | ||
63 | private Dictionary<UUID, Color4> m_colors = new Dictionary<UUID, Color4>(); | ||
64 | private bool m_useAntiAliasing = false; // TODO: Make this a config option | ||
65 | private bool m_Enabled = false; | ||
66 | |||
67 | #region IRegionModule Members | ||
68 | |||
69 | public void Initialise(IConfigSource source) | ||
70 | { | ||
71 | m_config = source; | ||
72 | |||
73 | IConfig startupConfig = m_config.Configs["Startup"]; | ||
74 | if (startupConfig.GetString("MapImageModule", "MapImageModule") != "Warp3DImageModule") | ||
75 | return; | ||
76 | |||
77 | m_Enabled = true; | ||
78 | } | ||
79 | |||
80 | public void AddRegion(Scene scene) | ||
81 | { | ||
82 | if (!m_Enabled) | ||
83 | return; | ||
84 | |||
85 | m_scene = scene; | ||
86 | |||
87 | List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory()); | ||
88 | if (renderers.Count > 0) | ||
89 | { | ||
90 | m_primMesher = RenderingLoader.LoadRenderer(renderers[0]); | ||
91 | m_log.Debug("[MAPTILE]: Loaded prim mesher " + m_primMesher.ToString()); | ||
92 | } | ||
93 | else | ||
94 | { | ||
95 | m_log.Debug("[MAPTILE]: No prim mesher loaded, prim rendering will be disabled"); | ||
96 | } | ||
97 | |||
98 | m_scene.RegisterModuleInterface<IMapImageGenerator>(this); | ||
99 | } | ||
100 | |||
101 | public void RegionLoaded(Scene scene) | ||
102 | { | ||
103 | } | ||
104 | |||
105 | public void RemoveRegion(Scene scene) | ||
106 | { | ||
107 | } | ||
108 | |||
109 | public void Close() | ||
110 | { | ||
111 | } | ||
112 | |||
113 | public string Name | ||
114 | { | ||
115 | get { return "Warp3DImageModule"; } | ||
116 | } | ||
117 | |||
118 | public Type ReplaceableInterface | ||
119 | { | ||
120 | get { return null; } | ||
121 | } | ||
122 | |||
123 | #endregion | ||
124 | |||
125 | #region IMapImageGenerator Members | ||
126 | |||
127 | public Bitmap CreateMapTile() | ||
128 | { | ||
129 | Vector3 camPos = new Vector3(127.5f, 127.5f, 221.7025033688163f); | ||
130 | Viewport viewport = new Viewport(camPos, -Vector3.UnitZ, 1024f, 0.1f, (int)Constants.RegionSize, (int)Constants.RegionSize, (float)Constants.RegionSize, (float)Constants.RegionSize); | ||
131 | return CreateMapTile(viewport, false); | ||
132 | } | ||
133 | |||
134 | public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float fov, int width, int height, bool useTextures) | ||
135 | { | ||
136 | Viewport viewport = new Viewport(camPos, camDir, fov, (float)Constants.RegionSize, 0.1f, width, height); | ||
137 | return CreateMapTile(viewport, useTextures); | ||
138 | } | ||
139 | |||
140 | public Bitmap CreateMapTile(Viewport viewport, bool useTextures) | ||
141 | { | ||
142 | bool drawPrimVolume = true; | ||
143 | bool textureTerrain = true; | ||
144 | |||
145 | try | ||
146 | { | ||
147 | IConfig startupConfig = m_config.Configs["Startup"]; | ||
148 | drawPrimVolume = startupConfig.GetBoolean("DrawPrimOnMapTile", drawPrimVolume); | ||
149 | textureTerrain = startupConfig.GetBoolean("TextureOnMapTile", textureTerrain); | ||
150 | } | ||
151 | catch | ||
152 | { | ||
153 | m_log.Warn("[MAPTILE]: Failed to load StartupConfig"); | ||
154 | } | ||
155 | |||
156 | m_colors.Clear(); | ||
157 | |||
158 | int width = viewport.Width; | ||
159 | int height = viewport.Height; | ||
160 | |||
161 | if (m_useAntiAliasing) | ||
162 | { | ||
163 | width *= 2; | ||
164 | height *= 2; | ||
165 | } | ||
166 | |||
167 | WarpRenderer renderer = new WarpRenderer(); | ||
168 | renderer.CreateScene(width, height); | ||
169 | renderer.Scene.autoCalcNormals = false; | ||
170 | |||
171 | #region Camera | ||
172 | |||
173 | warp_Vector pos = ConvertVector(viewport.Position); | ||
174 | pos.z -= 0.001f; // Works around an issue with the Warp3D camera | ||
175 | warp_Vector lookat = warp_Vector.add(ConvertVector(viewport.Position), ConvertVector(viewport.LookDirection)); | ||
176 | |||
177 | renderer.Scene.defaultCamera.setPos(pos); | ||
178 | renderer.Scene.defaultCamera.lookAt(lookat); | ||
179 | |||
180 | if (viewport.Orthographic) | ||
181 | { | ||
182 | renderer.Scene.defaultCamera.isOrthographic = true; | ||
183 | renderer.Scene.defaultCamera.orthoViewWidth = viewport.OrthoWindowWidth; | ||
184 | renderer.Scene.defaultCamera.orthoViewHeight = viewport.OrthoWindowHeight; | ||
185 | } | ||
186 | else | ||
187 | { | ||
188 | float fov = viewport.FieldOfView; | ||
189 | fov *= 1.75f; // FIXME: ??? | ||
190 | renderer.Scene.defaultCamera.setFov(fov); | ||
191 | } | ||
192 | |||
193 | #endregion Camera | ||
194 | |||
195 | renderer.Scene.addLight("Light1", new warp_Light(new warp_Vector(1.0f, 0.5f, 1f), 0xffffff, 0, 320, 40)); | ||
196 | renderer.Scene.addLight("Light2", new warp_Light(new warp_Vector(-1f, -1f, 1f), 0xffffff, 0, 100, 40)); | ||
197 | |||
198 | CreateWater(renderer); | ||
199 | CreateTerrain(renderer, textureTerrain); | ||
200 | if (drawPrimVolume) | ||
201 | CreateAllPrims(renderer, useTextures); | ||
202 | |||
203 | renderer.Render(); | ||
204 | Bitmap bitmap = renderer.Scene.getImage(); | ||
205 | |||
206 | if (m_useAntiAliasing) | ||
207 | { | ||
208 | using (Bitmap origBitmap = bitmap) | ||
209 | bitmap = ImageUtils.ResizeImage(origBitmap, viewport.Width, viewport.Height); | ||
210 | } | ||
211 | |||
212 | return bitmap; | ||
213 | } | ||
214 | |||
215 | public byte[] WriteJpeg2000Image() | ||
216 | { | ||
217 | try | ||
218 | { | ||
219 | using (Bitmap mapbmp = CreateMapTile()) | ||
220 | return OpenJPEG.EncodeFromImage(mapbmp, true); | ||
221 | } | ||
222 | catch (Exception e) | ||
223 | { | ||
224 | // JPEG2000 encoder failed | ||
225 | m_log.Error("[MAPTILE]: Failed generating terrain map: " + e); | ||
226 | } | ||
227 | |||
228 | return null; | ||
229 | } | ||
230 | |||
231 | #endregion | ||
232 | |||
233 | #region Rendering Methods | ||
234 | |||
235 | private void CreateWater(WarpRenderer renderer) | ||
236 | { | ||
237 | float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight; | ||
238 | |||
239 | renderer.AddPlane("Water", 256f * 0.5f); | ||
240 | renderer.Scene.sceneobject("Water").setPos(127.5f, waterHeight, 127.5f); | ||
241 | |||
242 | renderer.AddMaterial("WaterColor", ConvertColor(WATER_COLOR)); | ||
243 | renderer.Scene.material("WaterColor").setReflectivity(0); // match water color with standard map module thanks lkalif | ||
244 | renderer.Scene.material("WaterColor").setTransparency((byte)((1f - WATER_COLOR.A) * 255f)); | ||
245 | renderer.SetObjectMaterial("Water", "WaterColor"); | ||
246 | } | ||
247 | |||
248 | private void CreateTerrain(WarpRenderer renderer, bool textureTerrain) | ||
249 | { | ||
250 | ITerrainChannel terrain = m_scene.Heightmap; | ||
251 | float[] heightmap = terrain.GetFloatsSerialised(); | ||
252 | |||
253 | warp_Object obj = new warp_Object(256 * 256, 255 * 255 * 2); | ||
254 | |||
255 | for (int y = 0; y < 256; y++) | ||
256 | { | ||
257 | for (int x = 0; x < 256; x++) | ||
258 | { | ||
259 | int v = y * 256 + x; | ||
260 | float height = heightmap[v]; | ||
261 | |||
262 | warp_Vector pos = ConvertVector(new Vector3(x, y, height)); | ||
263 | obj.addVertex(new warp_Vertex(pos, (float)x / 255f, (float)(255 - y) / 255f)); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | for (int y = 0; y < 256; y++) | ||
268 | { | ||
269 | for (int x = 0; x < 256; x++) | ||
270 | { | ||
271 | if (x < 255 && y < 255) | ||
272 | { | ||
273 | int v = y * 256 + x; | ||
274 | |||
275 | // Normal | ||
276 | Vector3 v1 = new Vector3(x, y, heightmap[y * 256 + x]); | ||
277 | Vector3 v2 = new Vector3(x + 1, y, heightmap[y * 256 + x + 1]); | ||
278 | Vector3 v3 = new Vector3(x, y + 1, heightmap[(y + 1) * 256 + x]); | ||
279 | warp_Vector norm = ConvertVector(SurfaceNormal(v1, v2, v3)); | ||
280 | norm = norm.reverse(); | ||
281 | obj.vertex(v).n = norm; | ||
282 | |||
283 | // Triangle 1 | ||
284 | obj.addTriangle( | ||
285 | v, | ||
286 | v + 1, | ||
287 | v + 256); | ||
288 | |||
289 | // Triangle 2 | ||
290 | obj.addTriangle( | ||
291 | v + 256 + 1, | ||
292 | v + 256, | ||
293 | v + 1); | ||
294 | } | ||
295 | } | ||
296 | } | ||
297 | |||
298 | renderer.Scene.addObject("Terrain", obj); | ||
299 | |||
300 | UUID[] textureIDs = new UUID[4]; | ||
301 | float[] startHeights = new float[4]; | ||
302 | float[] heightRanges = new float[4]; | ||
303 | |||
304 | RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings; | ||
305 | |||
306 | textureIDs[0] = regionInfo.TerrainTexture1; | ||
307 | textureIDs[1] = regionInfo.TerrainTexture2; | ||
308 | textureIDs[2] = regionInfo.TerrainTexture3; | ||
309 | textureIDs[3] = regionInfo.TerrainTexture4; | ||
310 | |||
311 | startHeights[0] = (float)regionInfo.Elevation1SW; | ||
312 | startHeights[1] = (float)regionInfo.Elevation1NW; | ||
313 | startHeights[2] = (float)regionInfo.Elevation1SE; | ||
314 | startHeights[3] = (float)regionInfo.Elevation1NE; | ||
315 | |||
316 | heightRanges[0] = (float)regionInfo.Elevation2SW; | ||
317 | heightRanges[1] = (float)regionInfo.Elevation2NW; | ||
318 | heightRanges[2] = (float)regionInfo.Elevation2SE; | ||
319 | heightRanges[3] = (float)regionInfo.Elevation2NE; | ||
320 | |||
321 | uint globalX, globalY; | ||
322 | Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out globalY); | ||
323 | |||
324 | warp_Texture texture; | ||
325 | |||
326 | using ( | ||
327 | Bitmap image | ||
328 | = TerrainSplat.Splat( | ||
329 | heightmap, textureIDs, startHeights, heightRanges, | ||
330 | new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain)) | ||
331 | { | ||
332 | texture = new warp_Texture(image); | ||
333 | } | ||
334 | |||
335 | warp_Material material = new warp_Material(texture); | ||
336 | material.setReflectivity(50); | ||
337 | renderer.Scene.addMaterial("TerrainColor", material); | ||
338 | renderer.Scene.material("TerrainColor").setReflectivity(0); // reduces tile seams a bit thanks lkalif | ||
339 | renderer.SetObjectMaterial("Terrain", "TerrainColor"); | ||
340 | } | ||
341 | |||
342 | private void CreateAllPrims(WarpRenderer renderer, bool useTextures) | ||
343 | { | ||
344 | if (m_primMesher == null) | ||
345 | return; | ||
346 | |||
347 | m_scene.ForEachSOG( | ||
348 | delegate(SceneObjectGroup group) | ||
349 | { | ||
350 | CreatePrim(renderer, group.RootPart, useTextures); | ||
351 | foreach (SceneObjectPart child in group.Parts) | ||
352 | CreatePrim(renderer, child, useTextures); | ||
353 | } | ||
354 | ); | ||
355 | } | ||
356 | |||
357 | private void CreatePrim(WarpRenderer renderer, SceneObjectPart prim, | ||
358 | bool useTextures) | ||
359 | { | ||
360 | const float MIN_SIZE = 2f; | ||
361 | |||
362 | if ((PCode)prim.Shape.PCode != PCode.Prim) | ||
363 | return; | ||
364 | if (prim.Scale.LengthSquared() < MIN_SIZE * MIN_SIZE) | ||
365 | return; | ||
366 | |||
367 | Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset); | ||
368 | FacetedMesh renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium); | ||
369 | if (renderMesh == null) | ||
370 | return; | ||
371 | |||
372 | warp_Vector primPos = ConvertVector(prim.GetWorldPosition()); | ||
373 | warp_Quaternion primRot = ConvertQuaternion(prim.RotationOffset); | ||
374 | |||
375 | warp_Matrix m = warp_Matrix.quaternionMatrix(primRot); | ||
376 | |||
377 | if (prim.ParentID != 0) | ||
378 | { | ||
379 | SceneObjectGroup group = m_scene.SceneGraph.GetGroupByPrim(prim.LocalId); | ||
380 | if (group != null) | ||
381 | m.transform(warp_Matrix.quaternionMatrix(ConvertQuaternion(group.RootPart.RotationOffset))); | ||
382 | } | ||
383 | |||
384 | warp_Vector primScale = ConvertVector(prim.Scale); | ||
385 | |||
386 | string primID = prim.UUID.ToString(); | ||
387 | |||
388 | // Create the prim faces | ||
389 | // TODO: Implement the useTextures flag behavior | ||
390 | for (int i = 0; i < renderMesh.Faces.Count; i++) | ||
391 | { | ||
392 | Face face = renderMesh.Faces[i]; | ||
393 | string meshName = primID + "-Face-" + i.ToString(); | ||
394 | |||
395 | // Avoid adding duplicate meshes to the scene | ||
396 | if (renderer.Scene.objectData.ContainsKey(meshName)) | ||
397 | { | ||
398 | continue; | ||
399 | } | ||
400 | |||
401 | warp_Object faceObj = new warp_Object(face.Vertices.Count, face.Indices.Count / 3); | ||
402 | |||
403 | for (int j = 0; j < face.Vertices.Count; j++) | ||
404 | { | ||
405 | Vertex v = face.Vertices[j]; | ||
406 | |||
407 | warp_Vector pos = ConvertVector(v.Position); | ||
408 | warp_Vector norm = ConvertVector(v.Normal); | ||
409 | |||
410 | if (prim.Shape.SculptTexture == UUID.Zero) | ||
411 | norm = norm.reverse(); | ||
412 | warp_Vertex vert = new warp_Vertex(pos, norm, v.TexCoord.X, v.TexCoord.Y); | ||
413 | |||
414 | faceObj.addVertex(vert); | ||
415 | } | ||
416 | |||
417 | for (int j = 0; j < face.Indices.Count; j += 3) | ||
418 | { | ||
419 | faceObj.addTriangle( | ||
420 | face.Indices[j + 0], | ||
421 | face.Indices[j + 1], | ||
422 | face.Indices[j + 2]); | ||
423 | } | ||
424 | |||
425 | Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i); | ||
426 | Color4 faceColor = GetFaceColor(teFace); | ||
427 | string materialName = GetOrCreateMaterial(renderer, faceColor); | ||
428 | |||
429 | faceObj.transform(m); | ||
430 | faceObj.setPos(primPos); | ||
431 | faceObj.scaleSelf(primScale.x, primScale.y, primScale.z); | ||
432 | |||
433 | renderer.Scene.addObject(meshName, faceObj); | ||
434 | |||
435 | renderer.SetObjectMaterial(meshName, materialName); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | private Color4 GetFaceColor(Primitive.TextureEntryFace face) | ||
440 | { | ||
441 | Color4 color; | ||
442 | |||
443 | if (face.TextureID == UUID.Zero) | ||
444 | return face.RGBA; | ||
445 | |||
446 | if (!m_colors.TryGetValue(face.TextureID, out color)) | ||
447 | { | ||
448 | bool fetched = false; | ||
449 | |||
450 | // Attempt to fetch the texture metadata | ||
451 | UUID metadataID = UUID.Combine(face.TextureID, TEXTURE_METADATA_MAGIC); | ||
452 | AssetBase metadata = m_scene.AssetService.GetCached(metadataID.ToString()); | ||
453 | if (metadata != null) | ||
454 | { | ||
455 | OSDMap map = null; | ||
456 | try { map = OSDParser.Deserialize(metadata.Data) as OSDMap; } catch { } | ||
457 | |||
458 | if (map != null) | ||
459 | { | ||
460 | color = map["X-JPEG2000-RGBA"].AsColor4(); | ||
461 | fetched = true; | ||
462 | } | ||
463 | } | ||
464 | |||
465 | if (!fetched) | ||
466 | { | ||
467 | // Fetch the texture, decode and get the average color, | ||
468 | // then save it to a temporary metadata asset | ||
469 | AssetBase textureAsset = m_scene.AssetService.Get(face.TextureID.ToString()); | ||
470 | if (textureAsset != null) | ||
471 | { | ||
472 | int width, height; | ||
473 | color = GetAverageColor(textureAsset.FullID, textureAsset.Data, out width, out height); | ||
474 | |||
475 | OSDMap data = new OSDMap { { "X-JPEG2000-RGBA", OSD.FromColor4(color) } }; | ||
476 | metadata = new AssetBase | ||
477 | { | ||
478 | Data = System.Text.Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(data)), | ||
479 | Description = "Metadata for JPEG2000 texture " + face.TextureID.ToString(), | ||
480 | Flags = AssetFlags.Collectable, | ||
481 | FullID = metadataID, | ||
482 | ID = metadataID.ToString(), | ||
483 | Local = true, | ||
484 | Temporary = true, | ||
485 | Name = String.Empty, | ||
486 | Type = (sbyte)AssetType.Unknown | ||
487 | }; | ||
488 | m_scene.AssetService.Store(metadata); | ||
489 | } | ||
490 | else | ||
491 | { | ||
492 | color = new Color4(0.5f, 0.5f, 0.5f, 1.0f); | ||
493 | } | ||
494 | } | ||
495 | |||
496 | m_colors[face.TextureID] = color; | ||
497 | } | ||
498 | |||
499 | return color * face.RGBA; | ||
500 | } | ||
501 | |||
502 | private string GetOrCreateMaterial(WarpRenderer renderer, Color4 color) | ||
503 | { | ||
504 | string name = color.ToString(); | ||
505 | |||
506 | warp_Material material = renderer.Scene.material(name); | ||
507 | if (material != null) | ||
508 | return name; | ||
509 | |||
510 | renderer.AddMaterial(name, ConvertColor(color)); | ||
511 | if (color.A < 1f) | ||
512 | renderer.Scene.material(name).setTransparency((byte)((1f - color.A) * 255f)); | ||
513 | return name; | ||
514 | } | ||
515 | |||
516 | #endregion Rendering Methods | ||
517 | |||
518 | #region Static Helpers | ||
519 | |||
520 | private static warp_Vector ConvertVector(Vector3 vector) | ||
521 | { | ||
522 | return new warp_Vector(vector.X, vector.Z, vector.Y); | ||
523 | } | ||
524 | |||
525 | private static warp_Quaternion ConvertQuaternion(Quaternion quat) | ||
526 | { | ||
527 | return new warp_Quaternion(quat.X, quat.Z, quat.Y, -quat.W); | ||
528 | } | ||
529 | |||
530 | private static int ConvertColor(Color4 color) | ||
531 | { | ||
532 | int c = warp_Color.getColor((byte)(color.R * 255f), (byte)(color.G * 255f), (byte)(color.B * 255f)); | ||
533 | if (color.A < 1f) | ||
534 | c |= (byte)(color.A * 255f) << 24; | ||
535 | |||
536 | return c; | ||
537 | } | ||
538 | |||
539 | private static Vector3 SurfaceNormal(Vector3 c1, Vector3 c2, Vector3 c3) | ||
540 | { | ||
541 | Vector3 edge1 = new Vector3(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); | ||
542 | Vector3 edge2 = new Vector3(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); | ||
543 | |||
544 | Vector3 normal = Vector3.Cross(edge1, edge2); | ||
545 | normal.Normalize(); | ||
546 | |||
547 | return normal; | ||
548 | } | ||
549 | |||
550 | public static Color4 GetAverageColor(UUID textureID, byte[] j2kData, out int width, out int height) | ||
551 | { | ||
552 | ulong r = 0; | ||
553 | ulong g = 0; | ||
554 | ulong b = 0; | ||
555 | ulong a = 0; | ||
556 | |||
557 | using (MemoryStream stream = new MemoryStream(j2kData)) | ||
558 | { | ||
559 | try | ||
560 | { | ||
561 | int pixelBytes; | ||
562 | |||
563 | using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream)) | ||
564 | { | ||
565 | width = bitmap.Width; | ||
566 | height = bitmap.Height; | ||
567 | |||
568 | BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat); | ||
569 | pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4; | ||
570 | |||
571 | // Sum up the individual channels | ||
572 | unsafe | ||
573 | { | ||
574 | if (pixelBytes == 4) | ||
575 | { | ||
576 | for (int y = 0; y < height; y++) | ||
577 | { | ||
578 | byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride); | ||
579 | |||
580 | for (int x = 0; x < width; x++) | ||
581 | { | ||
582 | b += row[x * pixelBytes + 0]; | ||
583 | g += row[x * pixelBytes + 1]; | ||
584 | r += row[x * pixelBytes + 2]; | ||
585 | a += row[x * pixelBytes + 3]; | ||
586 | } | ||
587 | } | ||
588 | } | ||
589 | else | ||
590 | { | ||
591 | for (int y = 0; y < height; y++) | ||
592 | { | ||
593 | byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride); | ||
594 | |||
595 | for (int x = 0; x < width; x++) | ||
596 | { | ||
597 | b += row[x * pixelBytes + 0]; | ||
598 | g += row[x * pixelBytes + 1]; | ||
599 | r += row[x * pixelBytes + 2]; | ||
600 | } | ||
601 | } | ||
602 | } | ||
603 | } | ||
604 | } | ||
605 | |||
606 | // Get the averages for each channel | ||
607 | const decimal OO_255 = 1m / 255m; | ||
608 | decimal totalPixels = (decimal)(width * height); | ||
609 | |||
610 | decimal rm = ((decimal)r / totalPixels) * OO_255; | ||
611 | decimal gm = ((decimal)g / totalPixels) * OO_255; | ||
612 | decimal bm = ((decimal)b / totalPixels) * OO_255; | ||
613 | decimal am = ((decimal)a / totalPixels) * OO_255; | ||
614 | |||
615 | if (pixelBytes == 3) | ||
616 | am = 1m; | ||
617 | |||
618 | return new Color4((float)rm, (float)gm, (float)bm, (float)am); | ||
619 | } | ||
620 | catch (Exception ex) | ||
621 | { | ||
622 | m_log.WarnFormat("[MAPTILE]: Error decoding JPEG2000 texture {0} ({1} bytes): {2}", textureID, j2kData.Length, ex.Message); | ||
623 | width = 0; | ||
624 | height = 0; | ||
625 | return new Color4(0.5f, 0.5f, 0.5f, 1.0f); | ||
626 | } | ||
627 | } | ||
628 | } | ||
629 | |||
630 | #endregion Static Helpers | ||
631 | } | ||
632 | |||
633 | public static class ImageUtils | ||
634 | { | ||
635 | /// <summary> | ||
636 | /// Performs bilinear interpolation between four values | ||
637 | /// </summary> | ||
638 | /// <param name="v00">First, or top left value</param> | ||
639 | /// <param name="v01">Second, or top right value</param> | ||
640 | /// <param name="v10">Third, or bottom left value</param> | ||
641 | /// <param name="v11">Fourth, or bottom right value</param> | ||
642 | /// <param name="xPercent">Interpolation value on the X axis, between 0.0 and 1.0</param> | ||
643 | /// <param name="yPercent">Interpolation value on fht Y axis, between 0.0 and 1.0</param> | ||
644 | /// <returns>The bilinearly interpolated result</returns> | ||
645 | public static float Bilinear(float v00, float v01, float v10, float v11, float xPercent, float yPercent) | ||
646 | { | ||
647 | return Utils.Lerp(Utils.Lerp(v00, v01, xPercent), Utils.Lerp(v10, v11, xPercent), yPercent); | ||
648 | } | ||
649 | |||
650 | /// <summary> | ||
651 | /// Performs a high quality image resize | ||
652 | /// </summary> | ||
653 | /// <param name="image">Image to resize</param> | ||
654 | /// <param name="width">New width</param> | ||
655 | /// <param name="height">New height</param> | ||
656 | /// <returns>Resized image</returns> | ||
657 | public static Bitmap ResizeImage(Image image, int width, int height) | ||
658 | { | ||
659 | Bitmap result = new Bitmap(width, height); | ||
660 | |||
661 | using (Graphics graphics = Graphics.FromImage(result)) | ||
662 | { | ||
663 | graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; | ||
664 | graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; | ||
665 | graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; | ||
666 | graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; | ||
667 | |||
668 | graphics.DrawImage(image, 0, 0, result.Width, result.Height); | ||
669 | } | ||
670 | |||
671 | return result; | ||
672 | } | ||
673 | } | ||
674 | } \ No newline at end of file | ||