aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules/Materials
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/OptionalModules/Materials')
-rw-r--r--OpenSim/Region/OptionalModules/Materials/MaterialsDemoModule.cs657
-rw-r--r--OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs590
2 files changed, 590 insertions, 657 deletions
diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsDemoModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsDemoModule.cs
deleted file mode 100644
index d8f5563..0000000
--- a/OpenSim/Region/OptionalModules/Materials/MaterialsDemoModule.cs
+++ /dev/null
@@ -1,657 +0,0 @@
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.IO;
31using System.Reflection;
32using System.Security.Cryptography; // for computing md5 hash
33using log4net;
34using Mono.Addins;
35using Nini.Config;
36
37using OpenMetaverse;
38using OpenMetaverse.StructuredData;
39
40using OpenSim.Framework;
41using OpenSim.Framework.Servers;
42using OpenSim.Framework.Servers.HttpServer;
43using OpenSim.Region.Framework.Interfaces;
44using OpenSim.Region.Framework.Scenes;
45
46using Ionic.Zlib;
47
48// You will need to uncomment these lines if you are adding a region module to some other assembly which does not already
49// specify its assembly. Otherwise, the region modules in the assembly will not be picked up when OpenSimulator scans
50// the available DLLs
51//[assembly: Addin("MaterialsDemoModule", "1.0")]
52//[assembly: AddinDependency("OpenSim", "0.5")]
53
54namespace OpenSim.Region.OptionalModules.MaterialsDemoModule
55{
56 /// <summary>
57 ///
58 // # # ## ##### # # # # # ####
59 // # # # # # # ## # # ## # # #
60 // # # # # # # # # # # # # # #
61 // # ## # ###### ##### # # # # # # # # ###
62 // ## ## # # # # # ## # # ## # #
63 // # # # # # # # # # # # ####
64 //
65 // THIS MODULE IS FOR EXPERIMENTAL USE ONLY AND MAY CAUSE REGION OR ASSET CORRUPTION!
66 //
67 ////////////// WARNING //////////////////////////////////////////////////////////////////
68 /// This is an *Experimental* module for developing support for materials-capable viewers
69 /// This module should NOT be used in a production environment! It may cause data corruption and
70 /// viewer crashes. It should be only used to evaluate implementations of materials.
71 ///
72 /// Materials are persisted via SceneObjectPart.dynattrs. This is a relatively new feature
73 /// of OpenSimulator and is not field proven at the time this module was written. Persistence
74 /// may fail or become corrupt and this could cause viewer crashes due to erroneous materials
75 /// data being sent to viewers. Materials descriptions might survive IAR, OAR, or other means
76 /// of archiving however the texture resources used by these materials probably will not as they
77 /// may not be adequately referenced to ensure proper archiving.
78 ///
79 ///
80 ///
81 /// To enable this module, add this string at the bottom of OpenSim.ini:
82 /// [MaterialsDemoModule]
83 ///
84 /// </summary>
85 ///
86
87 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MaterialsDemoModule")]
88 public class MaterialsDemoModule : INonSharedRegionModule
89 {
90 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
91
92 public string Name { get { return "MaterialsDemoModule"; } }
93
94 public Type ReplaceableInterface { get { return null; } }
95
96 private Scene m_scene = null;
97 private bool m_enabled = false;
98
99 public Dictionary<UUID, OSDMap> m_knownMaterials = new Dictionary<UUID, OSDMap>();
100
101 public void Initialise(IConfigSource source)
102 {
103 m_enabled = (source.Configs["MaterialsDemoModule"] != null);
104 if (!m_enabled)
105 return;
106
107 m_log.DebugFormat("[MaterialsDemoModule]: INITIALIZED MODULE");
108 }
109
110 public void Close()
111 {
112 if (!m_enabled)
113 return;
114
115 m_log.DebugFormat("[MaterialsDemoModule]: CLOSED MODULE");
116 }
117
118 public void AddRegion(Scene scene)
119 {
120 if (!m_enabled)
121 return;
122
123 m_log.DebugFormat("[MaterialsDemoModule]: REGION {0} ADDED", scene.RegionInfo.RegionName);
124
125 m_scene = scene;
126 m_scene.EventManager.OnRegisterCaps += OnRegisterCaps;
127 m_scene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene;
128// m_scene.EventManager.OnGatherUuids += GatherMaterialsUuids;
129 }
130
131 void EventManager_OnObjectAddedToScene(SceneObjectGroup obj)
132 {
133 foreach (var part in obj.Parts)
134 if (part != null)
135 GetStoredMaterialsForPart(part);
136 }
137
138 void OnRegisterCaps(OpenMetaverse.UUID agentID, OpenSim.Framework.Capabilities.Caps caps)
139 {
140 string capsBase = "/CAPS/" + caps.CapsObjectPath;
141
142 IRequestHandler renderMaterialsPostHandler
143 = new RestStreamHandler("POST", capsBase + "/", RenderMaterialsPostCap, "RenderMaterials", null);
144 caps.RegisterHandler("RenderMaterials", renderMaterialsPostHandler);
145
146 // OpenSimulator CAPs infrastructure seems to be somewhat hostile towards any CAP that requires both GET
147 // and POST handlers, (at least at the time this was originally written), so we first set up a POST
148 // handler normally and then add a GET handler via MainServer
149
150 IRequestHandler renderMaterialsGetHandler
151 = new RestStreamHandler("GET", capsBase + "/", RenderMaterialsGetCap, "RenderMaterials", null);
152 MainServer.Instance.AddStreamHandler(renderMaterialsGetHandler);
153
154 // materials viewer seems to use either POST or PUT, so assign POST handler for PUT as well
155 IRequestHandler renderMaterialsPutHandler
156 = new RestStreamHandler("PUT", capsBase + "/", RenderMaterialsPostCap, "RenderMaterials", null);
157 MainServer.Instance.AddStreamHandler(renderMaterialsPutHandler);
158 }
159
160 public void RemoveRegion(Scene scene)
161 {
162 if (!m_enabled)
163 return;
164
165 m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps;
166 m_scene.EventManager.OnObjectAddedToScene -= EventManager_OnObjectAddedToScene;
167// m_scene.EventManager.OnGatherUuids -= GatherMaterialsUuids;
168
169 m_log.DebugFormat("[MaterialsDemoModule]: REGION {0} REMOVED", scene.RegionInfo.RegionName);
170 }
171
172 public void RegionLoaded(Scene scene)
173 {
174 }
175
176 OSDMap GetMaterial(UUID id)
177 {
178 OSDMap map = null;
179 lock (m_knownMaterials)
180 {
181 if (m_knownMaterials.ContainsKey(id))
182 {
183 map = new OSDMap();
184 map["ID"] = OSD.FromBinary(id.GetBytes());
185 map["Material"] = m_knownMaterials[id];
186 }
187 }
188 return map;
189 }
190
191 void GetStoredMaterialsForPart(SceneObjectPart part)
192 {
193 OSD OSMaterials = null;
194 OSDArray matsArr = null;
195
196 if (part.DynAttrs == null)
197 {
198 m_log.Warn("[MaterialsDemoModule]: NULL DYNATTRS :( ");
199 }
200
201 lock (part.DynAttrs)
202 {
203 if (part.DynAttrs.ContainsStore("OpenSim", "Materials"))
204 {
205 OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials");
206
207 if (materialsStore == null)
208 return;
209
210 materialsStore.TryGetValue("Materials", out OSMaterials);
211 }
212
213 if (OSMaterials != null && OSMaterials is OSDArray)
214 matsArr = OSMaterials as OSDArray;
215 else
216 return;
217 }
218
219 m_log.Info("[MaterialsDemoModule]: OSMaterials: " + OSDParser.SerializeJsonString(OSMaterials));
220
221 if (matsArr == null)
222 {
223 m_log.Info("[MaterialsDemoModule]: matsArr is null :( ");
224 return;
225 }
226
227 foreach (OSD elemOsd in matsArr)
228 {
229 if (elemOsd != null && elemOsd is OSDMap)
230 {
231 OSDMap matMap = elemOsd as OSDMap;
232 if (matMap.ContainsKey("ID") && matMap.ContainsKey("Material"))
233 {
234 try
235 {
236 lock (m_knownMaterials)
237 m_knownMaterials[matMap["ID"].AsUUID()] = (OSDMap)matMap["Material"];
238 }
239 catch (Exception e)
240 {
241 m_log.Warn("[MaterialsDemoModule]: exception decoding persisted material: " + e.ToString());
242 }
243 }
244 }
245 }
246 }
247
248 void StoreMaterialsForPart(SceneObjectPart part)
249 {
250 try
251 {
252 if (part == null || part.Shape == null)
253 return;
254
255 Dictionary<UUID, OSDMap> mats = new Dictionary<UUID, OSDMap>();
256
257 Primitive.TextureEntry te = part.Shape.Textures;
258
259 if (te.DefaultTexture != null)
260 {
261 lock (m_knownMaterials)
262 {
263 if (m_knownMaterials.ContainsKey(te.DefaultTexture.MaterialID))
264 mats[te.DefaultTexture.MaterialID] = m_knownMaterials[te.DefaultTexture.MaterialID];
265 }
266 }
267
268 if (te.FaceTextures != null)
269 {
270 foreach (var face in te.FaceTextures)
271 {
272 if (face != null)
273 {
274 lock (m_knownMaterials)
275 {
276 if (m_knownMaterials.ContainsKey(face.MaterialID))
277 mats[face.MaterialID] = m_knownMaterials[face.MaterialID];
278 }
279 }
280 }
281 }
282 if (mats.Count == 0)
283 return;
284
285 OSDArray matsArr = new OSDArray();
286 foreach (KeyValuePair<UUID, OSDMap> kvp in mats)
287 {
288 OSDMap matOsd = new OSDMap();
289 matOsd["ID"] = OSD.FromUUID(kvp.Key);
290 matOsd["Material"] = kvp.Value;
291 matsArr.Add(matOsd);
292 }
293
294 OSDMap OSMaterials = new OSDMap();
295 OSMaterials["Materials"] = matsArr;
296
297 lock (part.DynAttrs)
298 part.DynAttrs.SetStore("OpenSim", "Materials", OSMaterials);
299 }
300 catch (Exception e)
301 {
302 m_log.Warn("[MaterialsDemoModule]: exception in StoreMaterialsForPart(): " + e.ToString());
303 }
304 }
305
306 public string RenderMaterialsPostCap(string request, string path,
307 string param, IOSHttpRequest httpRequest,
308 IOSHttpResponse httpResponse)
309 {
310 m_log.Debug("[MaterialsDemoModule]: POST cap handler");
311
312 OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request);
313 OSDMap resp = new OSDMap();
314
315 OSDMap materialsFromViewer = null;
316
317 OSDArray respArr = new OSDArray();
318
319 if (req.ContainsKey("Zipped"))
320 {
321 OSD osd = null;
322
323 byte[] inBytes = req["Zipped"].AsBinary();
324
325 try
326 {
327 osd = ZDecompressBytesToOsd(inBytes);
328
329 if (osd != null)
330 {
331 if (osd is OSDArray) // assume array of MaterialIDs designating requested material entries
332 {
333 foreach (OSD elem in (OSDArray)osd)
334 {
335
336 try
337 {
338 UUID id = new UUID(elem.AsBinary(), 0);
339
340 lock (m_knownMaterials)
341 {
342 if (m_knownMaterials.ContainsKey(id))
343 {
344 m_log.Info("[MaterialsDemoModule]: request for known material ID: " + id.ToString());
345 OSDMap matMap = new OSDMap();
346 matMap["ID"] = OSD.FromBinary(id.GetBytes());
347
348 matMap["Material"] = m_knownMaterials[id];
349 respArr.Add(matMap);
350 }
351 else
352 m_log.Info("[MaterialsDemoModule]: request for UNKNOWN material ID: " + id.ToString());
353 }
354 }
355 catch (Exception e)
356 {
357 // report something here?
358 continue;
359 }
360 }
361 }
362 else if (osd is OSDMap) // reqest to assign a material
363 {
364 materialsFromViewer = osd as OSDMap;
365
366 if (materialsFromViewer.ContainsKey("FullMaterialsPerFace"))
367 {
368 OSD matsOsd = materialsFromViewer["FullMaterialsPerFace"];
369 if (matsOsd is OSDArray)
370 {
371 OSDArray matsArr = matsOsd as OSDArray;
372
373 try
374 {
375 foreach (OSDMap matsMap in matsArr)
376 {
377 m_log.Debug("[MaterialsDemoModule]: processing matsMap: " + OSDParser.SerializeJsonString(matsMap));
378
379 uint matLocalID = 0;
380 try { matLocalID = matsMap["ID"].AsUInteger(); }
381 catch (Exception e) { m_log.Warn("[MaterialsDemoModule]: cannot decode \"ID\" from matsMap: " + e.Message); }
382 m_log.Debug("[MaterialsDemoModule]: matLocalId: " + matLocalID.ToString());
383
384
385 OSDMap mat = null;
386 try { mat = matsMap["Material"] as OSDMap; }
387 catch (Exception e) { m_log.Warn("[MaterialsDemoModule]: cannot decode \"Material\" from matsMap: " + e.Message); }
388 m_log.Debug("[MaterialsDemoModule]: mat: " + OSDParser.SerializeJsonString(mat));
389
390 UUID id = HashOsd(mat);
391 lock (m_knownMaterials)
392 m_knownMaterials[id] = mat;
393
394
395 var sop = m_scene.GetSceneObjectPart(matLocalID);
396 if (sop == null)
397 m_log.Debug("[MaterialsDemoModule]: null SOP for localId: " + matLocalID.ToString());
398 else
399 {
400 var te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length);
401
402 if (te == null)
403 {
404 m_log.Debug("[MaterialsDemoModule]: null TextureEntry for localId: " + matLocalID.ToString());
405 }
406 else
407 {
408 int face = -1;
409
410 if (matsMap.ContainsKey("Face"))
411 {
412 face = matsMap["Face"].AsInteger();
413 if (te.FaceTextures == null) // && face == 0)
414 {
415 if (te.DefaultTexture == null)
416 m_log.Debug("[MaterialsDemoModule]: te.DefaultTexture is null");
417 else
418 te.DefaultTexture.MaterialID = id;
419 }
420 else
421 {
422 if (te.FaceTextures.Length >= face - 1)
423 {
424 if (te.FaceTextures[face] == null)
425 te.DefaultTexture.MaterialID = id;
426 else
427 te.FaceTextures[face].MaterialID = id;
428 }
429 }
430 }
431 else
432 {
433 if (te.DefaultTexture != null)
434 te.DefaultTexture.MaterialID = id;
435 }
436
437 m_log.Debug("[MaterialsDemoModule]: setting material ID for face " + face.ToString() + " to " + id.ToString());
438
439 //we cant use sop.UpdateTextureEntry(te); because it filters so do it manually
440
441 if (sop.ParentGroup != null)
442 {
443 sop.Shape.TextureEntry = te.GetBytes();
444 sop.TriggerScriptChangedEvent(Changed.TEXTURE);
445 sop.UpdateFlag = UpdateRequired.FULL;
446 sop.ParentGroup.HasGroupChanged = true;
447
448 sop.ScheduleFullUpdate();
449
450 StoreMaterialsForPart(sop);
451 }
452 }
453 }
454 }
455 }
456 catch (Exception e)
457 {
458 m_log.Warn("[MaterialsDemoModule]: exception processing received material: " + e.Message);
459 }
460 }
461 }
462 }
463 }
464
465 }
466 catch (Exception e)
467 {
468 m_log.Warn("[MaterialsDemoModule]: exception decoding zipped CAP payload: " + e.Message);
469 //return "";
470 }
471 m_log.Debug("[MaterialsDemoModule]: knownMaterials.Count: " + m_knownMaterials.Count.ToString());
472 }
473
474
475 resp["Zipped"] = ZCompressOSD(respArr, false);
476 string response = OSDParser.SerializeLLSDXmlString(resp);
477
478 //m_log.Debug("[MaterialsDemoModule]: cap request: " + request);
479 m_log.Debug("[MaterialsDemoModule]: cap request (zipped portion): " + ZippedOsdBytesToString(req["Zipped"].AsBinary()));
480 m_log.Debug("[MaterialsDemoModule]: cap response: " + response);
481 return response;
482 }
483
484
485 public string RenderMaterialsGetCap(string request, string path,
486 string param, IOSHttpRequest httpRequest,
487 IOSHttpResponse httpResponse)
488 {
489 m_log.Debug("[MaterialsDemoModule]: GET cap handler");
490
491 OSDMap resp = new OSDMap();
492 int matsCount = 0;
493 OSDArray allOsd = new OSDArray();
494
495 lock (m_knownMaterials)
496 {
497 foreach (KeyValuePair<UUID, OSDMap> kvp in m_knownMaterials)
498 {
499 OSDMap matMap = new OSDMap();
500
501 matMap["ID"] = OSD.FromBinary(kvp.Key.GetBytes());
502 matMap["Material"] = kvp.Value;
503 allOsd.Add(matMap);
504 matsCount++;
505 }
506 }
507
508 resp["Zipped"] = ZCompressOSD(allOsd, false);
509 m_log.Debug("[MaterialsDemoModule]: matsCount: " + matsCount.ToString());
510
511 return OSDParser.SerializeLLSDXmlString(resp);
512 }
513
514 static string ZippedOsdBytesToString(byte[] bytes)
515 {
516 try
517 {
518 return OSDParser.SerializeJsonString(ZDecompressBytesToOsd(bytes));
519 }
520 catch (Exception e)
521 {
522 return "ZippedOsdBytesToString caught an exception: " + e.ToString();
523 }
524 }
525
526 /// <summary>
527 /// computes a UUID by hashing a OSD object
528 /// </summary>
529 /// <param name="osd"></param>
530 /// <returns></returns>
531 private static UUID HashOsd(OSD osd)
532 {
533 using (var md5 = MD5.Create())
534 using (MemoryStream ms = new MemoryStream(OSDParser.SerializeLLSDBinary(osd, false)))
535 return new UUID(md5.ComputeHash(ms), 0);
536 }
537
538 public static OSD ZCompressOSD(OSD inOsd, bool useHeader)
539 {
540 OSD osd = null;
541
542 using (MemoryStream msSinkCompressed = new MemoryStream())
543 {
544 using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkCompressed,
545 Ionic.Zlib.CompressionMode.Compress, CompressionLevel.BestCompression, true))
546 {
547 CopyStream(new MemoryStream(OSDParser.SerializeLLSDBinary(inOsd, useHeader)), zOut);
548 zOut.Close();
549 }
550
551 msSinkCompressed.Seek(0L, SeekOrigin.Begin);
552 osd = OSD.FromBinary( msSinkCompressed.ToArray());
553 }
554
555 return osd;
556 }
557
558
559 public static OSD ZDecompressBytesToOsd(byte[] input)
560 {
561 OSD osd = null;
562
563 using (MemoryStream msSinkUnCompressed = new MemoryStream())
564 {
565 using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkUnCompressed, CompressionMode.Decompress, true))
566 {
567 CopyStream(new MemoryStream(input), zOut);
568 zOut.Close();
569 }
570 msSinkUnCompressed.Seek(0L, SeekOrigin.Begin);
571 osd = OSDParser.DeserializeLLSDBinary(msSinkUnCompressed.ToArray());
572 }
573
574 return osd;
575 }
576
577 static void CopyStream(System.IO.Stream input, System.IO.Stream output)
578 {
579 byte[] buffer = new byte[2048];
580 int len;
581 while ((len = input.Read(buffer, 0, 2048)) > 0)
582 {
583 output.Write(buffer, 0, len);
584 }
585
586 output.Flush();
587 }
588
589 // FIXME: This code is currently still in UuidGatherer since we cannot use Scene.EventManager as some
590 // calls to the gatherer are done for objects with no scene.
591// /// <summary>
592// /// Gather all of the texture asset UUIDs used to reference "Materials" such as normal and specular maps
593// /// </summary>
594// /// <param name="part"></param>
595// /// <param name="assetUuids"></param>
596// private void GatherMaterialsUuids(SceneObjectPart part, IDictionary<UUID, AssetType> assetUuids)
597// {
598// // scan thru the dynAttrs map of this part for any textures used as materials
599// OSD osdMaterials = null;
600//
601// lock (part.DynAttrs)
602// {
603// if (part.DynAttrs.ContainsStore("OpenSim", "Materials"))
604// {
605// OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials");
606// if (materialsStore == null)
607// return;
608//
609// materialsStore.TryGetValue("Materials", out osdMaterials);
610// }
611//
612// if (osdMaterials != null)
613// {
614// //m_log.Info("[UUID Gatherer]: found Materials: " + OSDParser.SerializeJsonString(osd));
615//
616// if (osdMaterials is OSDArray)
617// {
618// OSDArray matsArr = osdMaterials as OSDArray;
619// foreach (OSDMap matMap in matsArr)
620// {
621// try
622// {
623// if (matMap.ContainsKey("Material"))
624// {
625// OSDMap mat = matMap["Material"] as OSDMap;
626// if (mat.ContainsKey("NormMap"))
627// {
628// UUID normalMapId = mat["NormMap"].AsUUID();
629// if (normalMapId != UUID.Zero)
630// {
631// assetUuids[normalMapId] = AssetType.Texture;
632// //m_log.Info("[UUID Gatherer]: found normal map ID: " + normalMapId.ToString());
633// }
634// }
635// if (mat.ContainsKey("SpecMap"))
636// {
637// UUID specularMapId = mat["SpecMap"].AsUUID();
638// if (specularMapId != UUID.Zero)
639// {
640// assetUuids[specularMapId] = AssetType.Texture;
641// //m_log.Info("[UUID Gatherer]: found specular map ID: " + specularMapId.ToString());
642// }
643// }
644// }
645//
646// }
647// catch (Exception e)
648// {
649// m_log.Warn("[MaterialsDemoModule]: exception getting materials: " + e.Message);
650// }
651// }
652// }
653// }
654// }
655// }
656 }
657} \ No newline at end of file
diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs
new file mode 100644
index 0000000..afb788b
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs
@@ -0,0 +1,590 @@
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.IO;
31using System.Reflection;
32using System.Security.Cryptography; // for computing md5 hash
33using log4net;
34using Mono.Addins;
35using Nini.Config;
36
37using OpenMetaverse;
38using OpenMetaverse.StructuredData;
39
40using OpenSim.Framework;
41using OpenSim.Framework.Servers;
42using OpenSim.Framework.Servers.HttpServer;
43using OpenSim.Region.Framework.Interfaces;
44using OpenSim.Region.Framework.Scenes;
45using OpenSimAssetType = OpenSim.Framework.SLUtil.OpenSimAssetType;
46
47using Ionic.Zlib;
48
49// You will need to uncomment these lines if you are adding a region module to some other assembly which does not already
50// specify its assembly. Otherwise, the region modules in the assembly will not be picked up when OpenSimulator scans
51// the available DLLs
52//[assembly: Addin("MaterialsModule", "1.0")]
53//[assembly: AddinDependency("OpenSim", "0.5")]
54
55namespace OpenSim.Region.OptionalModules.Materials
56{
57 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MaterialsModule")]
58 public class MaterialsModule : INonSharedRegionModule
59 {
60 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
61
62 public string Name { get { return "MaterialsModule"; } }
63
64 public Type ReplaceableInterface { get { return null; } }
65
66 private Scene m_scene = null;
67 private bool m_enabled = false;
68
69 public Dictionary<UUID, OSDMap> m_regionMaterials = new Dictionary<UUID, OSDMap>();
70
71 public void Initialise(IConfigSource source)
72 {
73 IConfig config = source.Configs["Materials"];
74 if (config == null)
75 return;
76
77 m_enabled = config.GetBoolean("enable_materials", true);
78 if (!m_enabled)
79 return;
80
81 m_log.DebugFormat("[Materials]: Initialized");
82 }
83
84 public void Close()
85 {
86 if (!m_enabled)
87 return;
88 }
89
90 public void AddRegion(Scene scene)
91 {
92 if (!m_enabled)
93 return;
94
95 m_scene = scene;
96 m_scene.EventManager.OnRegisterCaps += OnRegisterCaps;
97 m_scene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene;
98 }
99
100 private void EventManager_OnObjectAddedToScene(SceneObjectGroup obj)
101 {
102 foreach (var part in obj.Parts)
103 if (part != null)
104 GetStoredMaterialsInPart(part);
105 }
106
107 private void OnRegisterCaps(OpenMetaverse.UUID agentID, OpenSim.Framework.Capabilities.Caps caps)
108 {
109 string capsBase = "/CAPS/" + caps.CapsObjectPath;
110
111 IRequestHandler renderMaterialsPostHandler
112 = new RestStreamHandler("POST", capsBase + "/",
113 (request, path, param, httpRequest, httpResponse)
114 => RenderMaterialsPostCap(request, agentID),
115 "RenderMaterials", null);
116 caps.RegisterHandler("RenderMaterials", renderMaterialsPostHandler);
117
118 // OpenSimulator CAPs infrastructure seems to be somewhat hostile towards any CAP that requires both GET
119 // and POST handlers, (at least at the time this was originally written), so we first set up a POST
120 // handler normally and then add a GET handler via MainServer
121
122 IRequestHandler renderMaterialsGetHandler
123 = new RestStreamHandler("GET", capsBase + "/",
124 (request, path, param, httpRequest, httpResponse)
125 => RenderMaterialsGetCap(request),
126 "RenderMaterials", null);
127 MainServer.Instance.AddStreamHandler(renderMaterialsGetHandler);
128
129 // materials viewer seems to use either POST or PUT, so assign POST handler for PUT as well
130 IRequestHandler renderMaterialsPutHandler
131 = new RestStreamHandler("PUT", capsBase + "/",
132 (request, path, param, httpRequest, httpResponse)
133 => RenderMaterialsPostCap(request, agentID),
134 "RenderMaterials", null);
135 MainServer.Instance.AddStreamHandler(renderMaterialsPutHandler);
136 }
137
138 public void RemoveRegion(Scene scene)
139 {
140 if (!m_enabled)
141 return;
142
143 m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps;
144 m_scene.EventManager.OnObjectAddedToScene -= EventManager_OnObjectAddedToScene;
145 }
146
147 public void RegionLoaded(Scene scene)
148 {
149 }
150
151 /// <summary>
152 /// Finds any legacy materials stored in DynAttrs that may exist for this part and add them to 'm_regionMaterials'.
153 /// </summary>
154 /// <param name="part"></param>
155 private void GetLegacyStoredMaterialsInPart(SceneObjectPart part)
156 {
157 if (part.DynAttrs == null)
158 return;
159
160 OSD OSMaterials = null;
161 OSDArray matsArr = null;
162
163 lock (part.DynAttrs)
164 {
165 if (part.DynAttrs.ContainsStore("OpenSim", "Materials"))
166 {
167 OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials");
168
169 if (materialsStore == null)
170 return;
171
172 materialsStore.TryGetValue("Materials", out OSMaterials);
173 }
174
175 if (OSMaterials != null && OSMaterials is OSDArray)
176 matsArr = OSMaterials as OSDArray;
177 else
178 return;
179 }
180
181 if (matsArr == null)
182 return;
183
184 foreach (OSD elemOsd in matsArr)
185 {
186 if (elemOsd != null && elemOsd is OSDMap)
187 {
188 OSDMap matMap = elemOsd as OSDMap;
189 if (matMap.ContainsKey("ID") && matMap.ContainsKey("Material"))
190 {
191 try
192 {
193 lock (m_regionMaterials)
194 m_regionMaterials[matMap["ID"].AsUUID()] = (OSDMap)matMap["Material"];
195 }
196 catch (Exception e)
197 {
198 m_log.Warn("[Materials]: exception decoding persisted legacy material: " + e.ToString());
199 }
200 }
201 }
202 }
203 }
204
205 /// <summary>
206 /// Find the materials used in the SOP, and add them to 'm_regionMaterials'.
207 /// </summary>
208 private void GetStoredMaterialsInPart(SceneObjectPart part)
209 {
210 if (part.Shape == null)
211 return;
212
213 var te = new Primitive.TextureEntry(part.Shape.TextureEntry, 0, part.Shape.TextureEntry.Length);
214 if (te == null)
215 return;
216
217 GetLegacyStoredMaterialsInPart(part);
218
219 GetStoredMaterialInFace(part, te.DefaultTexture);
220
221 foreach (Primitive.TextureEntryFace face in te.FaceTextures)
222 {
223 if (face != null)
224 GetStoredMaterialInFace(part, face);
225 }
226 }
227
228 /// <summary>
229 /// Find the materials used in one Face, and add them to 'm_regionMaterials'.
230 /// </summary>
231 private void GetStoredMaterialInFace(SceneObjectPart part, Primitive.TextureEntryFace face)
232 {
233 UUID id = face.MaterialID;
234 if (id == UUID.Zero)
235 return;
236
237 lock (m_regionMaterials)
238 {
239 if (m_regionMaterials.ContainsKey(id))
240 return;
241
242 byte[] data = m_scene.AssetService.GetData(id.ToString());
243 if (data == null)
244 {
245 m_log.WarnFormat("[Materials]: Prim \"{0}\" ({1}) contains unknown material ID {2}", part.Name, part.UUID, id);
246 return;
247 }
248
249 OSDMap mat;
250 try
251 {
252 mat = (OSDMap)OSDParser.DeserializeLLSDXml(data);
253 }
254 catch (Exception e)
255 {
256 m_log.WarnFormat("[Materials]: cannot decode material asset {0}: {1}", id, e.Message);
257 return;
258 }
259
260 m_regionMaterials[id] = mat;
261 }
262 }
263
264 public string RenderMaterialsPostCap(string request, UUID agentID)
265 {
266 OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request);
267 OSDMap resp = new OSDMap();
268
269 OSDMap materialsFromViewer = null;
270
271 OSDArray respArr = new OSDArray();
272
273 if (req.ContainsKey("Zipped"))
274 {
275 OSD osd = null;
276
277 byte[] inBytes = req["Zipped"].AsBinary();
278
279 try
280 {
281 osd = ZDecompressBytesToOsd(inBytes);
282
283 if (osd != null)
284 {
285 if (osd is OSDArray) // assume array of MaterialIDs designating requested material entries
286 {
287 foreach (OSD elem in (OSDArray)osd)
288 {
289 try
290 {
291 UUID id = new UUID(elem.AsBinary(), 0);
292
293 lock (m_regionMaterials)
294 {
295 if (m_regionMaterials.ContainsKey(id))
296 {
297 OSDMap matMap = new OSDMap();
298 matMap["ID"] = OSD.FromBinary(id.GetBytes());
299 matMap["Material"] = m_regionMaterials[id];
300 respArr.Add(matMap);
301 }
302 else
303 {
304 m_log.Warn("[Materials]: request for unknown material ID: " + id.ToString());
305
306 // Theoretically we could try to load the material from the assets service,
307 // but that shouldn't be necessary because the viewer should only request
308 // materials that exist in a prim on the region, and all of these materials
309 // are already stored in m_regionMaterials.
310 }
311 }
312 }
313 catch (Exception e)
314 {
315 m_log.Error("Error getting materials in response to viewer request", e);
316 continue;
317 }
318 }
319 }
320 else if (osd is OSDMap) // request to assign a material
321 {
322 materialsFromViewer = osd as OSDMap;
323
324 if (materialsFromViewer.ContainsKey("FullMaterialsPerFace"))
325 {
326 OSD matsOsd = materialsFromViewer["FullMaterialsPerFace"];
327 if (matsOsd is OSDArray)
328 {
329 OSDArray matsArr = matsOsd as OSDArray;
330
331 try
332 {
333 foreach (OSDMap matsMap in matsArr)
334 {
335 uint primLocalID = 0;
336 try {
337 primLocalID = matsMap["ID"].AsUInteger();
338 }
339 catch (Exception e) {
340 m_log.Warn("[Materials]: cannot decode \"ID\" from matsMap: " + e.Message);
341 continue;
342 }
343
344 OSDMap mat = null;
345 try
346 {
347 mat = matsMap["Material"] as OSDMap;
348 }
349 catch (Exception e)
350 {
351 m_log.Warn("[Materials]: cannot decode \"Material\" from matsMap: " + e.Message);
352 continue;
353 }
354
355 SceneObjectPart sop = m_scene.GetSceneObjectPart(primLocalID);
356 if (sop == null)
357 {
358 m_log.WarnFormat("[Materials]: SOP not found for localId: {0}", primLocalID.ToString());
359 continue;
360 }
361
362 if (!m_scene.Permissions.CanEditObject(sop.UUID, agentID))
363 {
364 m_log.WarnFormat("User {0} can't edit object {1} {2}", agentID, sop.Name, sop.UUID);
365 continue;
366 }
367
368 Primitive.TextureEntry te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length);
369 if (te == null)
370 {
371 m_log.WarnFormat("[Materials]: Error in TextureEntry for SOP {0} {1}", sop.Name, sop.UUID);
372 continue;
373 }
374
375
376 UUID id;
377 if (mat == null)
378 {
379 // This happens then the user removes a material from a prim
380 id = UUID.Zero;
381 }
382 else
383 {
384 id = StoreMaterialAsAsset(agentID, mat, sop);
385 }
386
387
388 int face = -1;
389
390 if (matsMap.ContainsKey("Face"))
391 {
392 face = matsMap["Face"].AsInteger();
393 Primitive.TextureEntryFace faceEntry = te.CreateFace((uint)face);
394 faceEntry.MaterialID = id;
395 }
396 else
397 {
398 if (te.DefaultTexture == null)
399 m_log.WarnFormat("[Materials]: TextureEntry.DefaultTexture is null in {0} {1}", sop.Name, sop.UUID);
400 else
401 te.DefaultTexture.MaterialID = id;
402 }
403
404 //m_log.DebugFormat("[Materials]: in \"{0}\" {1}, setting material ID for face {2} to {3}", sop.Name, sop.UUID, face, id);
405
406 // We can't use sop.UpdateTextureEntry(te) because it filters, so do it manually
407 sop.Shape.TextureEntry = te.GetBytes();
408
409 if (sop.ParentGroup != null)
410 {
411 sop.TriggerScriptChangedEvent(Changed.TEXTURE);
412 sop.UpdateFlag = UpdateRequired.FULL;
413 sop.ParentGroup.HasGroupChanged = true;
414 sop.ScheduleFullUpdate();
415 }
416 }
417 }
418 catch (Exception e)
419 {
420 m_log.Warn("[Materials]: exception processing received material ", e);
421 }
422 }
423 }
424 }
425 }
426
427 }
428 catch (Exception e)
429 {
430 m_log.Warn("[Materials]: exception decoding zipped CAP payload ", e);
431 //return "";
432 }
433 }
434
435
436 resp["Zipped"] = ZCompressOSD(respArr, false);
437 string response = OSDParser.SerializeLLSDXmlString(resp);
438
439 //m_log.Debug("[Materials]: cap request: " + request);
440 //m_log.Debug("[Materials]: cap request (zipped portion): " + ZippedOsdBytesToString(req["Zipped"].AsBinary()));
441 //m_log.Debug("[Materials]: cap response: " + response);
442 return response;
443 }
444
445 private UUID StoreMaterialAsAsset(UUID agentID, OSDMap mat, SceneObjectPart sop)
446 {
447 UUID id;
448 // Material UUID = hash of the material's data.
449 // This makes materials deduplicate across the entire grid (but isn't otherwise required).
450 byte[] data = System.Text.Encoding.ASCII.GetBytes(OSDParser.SerializeLLSDXmlString(mat));
451 using (var md5 = MD5.Create())
452 id = new UUID(md5.ComputeHash(data), 0);
453
454 lock (m_regionMaterials)
455 {
456 if (!m_regionMaterials.ContainsKey(id))
457 {
458 m_regionMaterials[id] = mat;
459
460 // This asset might exist already, but it's ok to try to store it again
461 string name = "Material " + ChooseMaterialName(mat, sop);
462 name = name.Substring(0, Math.Min(64, name.Length)).Trim();
463 AssetBase asset = new AssetBase(id, name, (sbyte)OpenSimAssetType.Material, agentID.ToString());
464 asset.Data = data;
465 m_scene.AssetService.Store(asset);
466 }
467 }
468 return id;
469 }
470
471 /// <summary>
472 /// Use heuristics to choose a good name for the material.
473 /// </summary>
474 private string ChooseMaterialName(OSDMap mat, SceneObjectPart sop)
475 {
476 UUID normMap = mat["NormMap"].AsUUID();
477 if (normMap != UUID.Zero)
478 {
479 AssetBase asset = m_scene.AssetService.GetCached(normMap.ToString());
480 if ((asset != null) && (asset.Name.Length > 0) && !asset.Name.Equals("From IAR"))
481 return asset.Name;
482 }
483
484 UUID specMap = mat["SpecMap"].AsUUID();
485 if (specMap != UUID.Zero)
486 {
487 AssetBase asset = m_scene.AssetService.GetCached(specMap.ToString());
488 if ((asset != null) && (asset.Name.Length > 0) && !asset.Name.Equals("From IAR"))
489 return asset.Name;
490 }
491
492 if (sop.Name != "Primitive")
493 return sop.Name;
494
495 if ((sop.ParentGroup != null) && (sop.ParentGroup.Name != "Primitive"))
496 return sop.ParentGroup.Name;
497
498 return "";
499 }
500
501
502 public string RenderMaterialsGetCap(string request)
503 {
504 OSDMap resp = new OSDMap();
505 int matsCount = 0;
506 OSDArray allOsd = new OSDArray();
507
508 lock (m_regionMaterials)
509 {
510 foreach (KeyValuePair<UUID, OSDMap> kvp in m_regionMaterials)
511 {
512 OSDMap matMap = new OSDMap();
513
514 matMap["ID"] = OSD.FromBinary(kvp.Key.GetBytes());
515 matMap["Material"] = kvp.Value;
516 allOsd.Add(matMap);
517 matsCount++;
518 }
519 }
520
521 resp["Zipped"] = ZCompressOSD(allOsd, false);
522
523 return OSDParser.SerializeLLSDXmlString(resp);
524 }
525
526 private static string ZippedOsdBytesToString(byte[] bytes)
527 {
528 try
529 {
530 return OSDParser.SerializeJsonString(ZDecompressBytesToOsd(bytes));
531 }
532 catch (Exception e)
533 {
534 return "ZippedOsdBytesToString caught an exception: " + e.ToString();
535 }
536 }
537
538 /// <summary>
539 /// computes a UUID by hashing a OSD object
540 /// </summary>
541 /// <param name="osd"></param>
542 /// <returns></returns>
543 private static UUID HashOsd(OSD osd)
544 {
545 byte[] data = OSDParser.SerializeLLSDBinary(osd, false);
546 using (var md5 = MD5.Create())
547 return new UUID(md5.ComputeHash(data), 0);
548 }
549
550 public static OSD ZCompressOSD(OSD inOsd, bool useHeader)
551 {
552 OSD osd = null;
553
554 byte[] data = OSDParser.SerializeLLSDBinary(inOsd, useHeader);
555
556 using (MemoryStream msSinkCompressed = new MemoryStream())
557 {
558 using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkCompressed,
559 Ionic.Zlib.CompressionMode.Compress, CompressionLevel.BestCompression, true))
560 {
561 zOut.Write(data, 0, data.Length);
562 }
563
564 msSinkCompressed.Seek(0L, SeekOrigin.Begin);
565 osd = OSD.FromBinary(msSinkCompressed.ToArray());
566 }
567
568 return osd;
569 }
570
571
572 public static OSD ZDecompressBytesToOsd(byte[] input)
573 {
574 OSD osd = null;
575
576 using (MemoryStream msSinkUnCompressed = new MemoryStream())
577 {
578 using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkUnCompressed, CompressionMode.Decompress, true))
579 {
580 zOut.Write(input, 0, input.Length);
581 }
582
583 msSinkUnCompressed.Seek(0L, SeekOrigin.Begin);
584 osd = OSDParser.DeserializeLLSDBinary(msSinkUnCompressed.ToArray());
585 }
586
587 return osd;
588 }
589 }
590}