aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules/Materials/MaterialsDemoModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/OptionalModules/Materials/MaterialsDemoModule.cs')
-rw-r--r--OpenSim/Region/OptionalModules/Materials/MaterialsDemoModule.cs579
1 files changed, 579 insertions, 0 deletions
diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsDemoModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsDemoModule.cs
new file mode 100644
index 0000000..4ab6609
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Materials/MaterialsDemoModule.cs
@@ -0,0 +1,579 @@
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 m_scene = scene;
125 m_scene.EventManager.OnRegisterCaps += new EventManager.RegisterCapsEvent(OnRegisterCaps);
126 m_scene.EventManager.OnObjectAddedToScene += new Action<SceneObjectGroup>(EventManager_OnObjectAddedToScene);
127 }
128
129 void EventManager_OnObjectAddedToScene(SceneObjectGroup obj)
130 {
131 foreach (var part in obj.Parts)
132 if (part != null)
133 GetStoredMaterialsForPart(part);
134 }
135
136 void OnRegisterCaps(OpenMetaverse.UUID agentID, OpenSim.Framework.Capabilities.Caps caps)
137 {
138 string capsBase = "/CAPS/" + caps.CapsObjectPath;
139
140 IRequestHandler renderMaterialsPostHandler = new RestStreamHandler("POST", capsBase + "/", RenderMaterialsPostCap);
141 caps.RegisterHandler("RenderMaterials", renderMaterialsPostHandler);
142
143 // OpenSimulator CAPs infrastructure seems to be somewhat hostile towards any CAP that requires both GET
144 // and POST handlers, (at least at the time this was originally written), so we first set up a POST
145 // handler normally and then add a GET handler via MainServer
146
147 IRequestHandler renderMaterialsGetHandler = new RestStreamHandler("GET", capsBase + "/", RenderMaterialsGetCap);
148 MainServer.Instance.AddStreamHandler(renderMaterialsGetHandler);
149
150 // materials viewer seems to use either POST or PUT, so assign POST handler for PUT as well
151 IRequestHandler renderMaterialsPutHandler = new RestStreamHandler("PUT", capsBase + "/", RenderMaterialsPostCap);
152 MainServer.Instance.AddStreamHandler(renderMaterialsPutHandler);
153 }
154
155 public void RemoveRegion(Scene scene)
156 {
157 if (!m_enabled)
158 return;
159
160 m_log.DebugFormat("[MaterialsDemoModule]: REGION {0} REMOVED", scene.RegionInfo.RegionName);
161 }
162
163 public void RegionLoaded(Scene scene)
164 {
165 }
166
167 OSDMap GetMaterial(UUID id)
168 {
169 OSDMap map = null;
170 if (m_knownMaterials.ContainsKey(id))
171 {
172 map = new OSDMap();
173 map["ID"] = OSD.FromBinary(id.GetBytes());
174 map["Material"] = m_knownMaterials[id];
175 }
176 return map;
177 }
178
179 void GetStoredMaterialsForPart(SceneObjectPart part)
180 {
181 OSDMap OSMaterials = null;
182 OSDArray matsArr = null;
183
184 if (part.DynAttrs == null)
185 {
186 m_log.Warn("[MaterialsDemoModule]: NULL DYNATTRS :( ");
187 }
188
189 lock (part.DynAttrs)
190 {
191 if (part.DynAttrs.ContainsKey("OS:Materials"))
192 OSMaterials = part.DynAttrs["OS:Materials"];
193 if (OSMaterials != null && OSMaterials.ContainsKey("Materials"))
194 {
195
196 OSD osd = OSMaterials["Materials"];
197 if (osd is OSDArray)
198 matsArr = osd as OSDArray;
199 }
200 }
201
202 if (OSMaterials == null)
203 return;
204
205 m_log.Info("[MaterialsDemoModule]: OSMaterials: " + OSDParser.SerializeJsonString(OSMaterials));
206
207
208 if (matsArr == null)
209 {
210 m_log.Info("[MaterialsDemoModule]: matsArr is null :( ");
211 return;
212 }
213
214 foreach (OSD elemOsd in matsArr)
215 {
216 if (elemOsd != null && elemOsd is OSDMap)
217 {
218
219 OSDMap matMap = elemOsd as OSDMap;
220 if (matMap.ContainsKey("ID") && matMap.ContainsKey("Material"))
221 {
222 try
223 {
224 m_knownMaterials[matMap["ID"].AsUUID()] = (OSDMap)matMap["Material"];
225 }
226 catch (Exception e)
227 {
228 m_log.Warn("[MaterialsDemoModule]: exception decoding persisted material: " + e.ToString());
229 }
230 }
231 }
232 }
233 }
234
235
236 void StoreMaterialsForPart(SceneObjectPart part)
237 {
238 try
239 {
240 if (part == null || part.Shape == null)
241 return;
242
243 Dictionary<UUID, OSDMap> mats = new Dictionary<UUID, OSDMap>();
244
245 Primitive.TextureEntry te = part.Shape.Textures;
246
247 if (te.DefaultTexture != null)
248 {
249 if (m_knownMaterials.ContainsKey(te.DefaultTexture.MaterialID))
250 mats[te.DefaultTexture.MaterialID] = m_knownMaterials[te.DefaultTexture.MaterialID];
251 }
252
253 if (te.FaceTextures != null)
254 {
255 foreach (var face in te.FaceTextures)
256 {
257 if (face != null)
258 {
259 if (m_knownMaterials.ContainsKey(face.MaterialID))
260 mats[face.MaterialID] = m_knownMaterials[face.MaterialID];
261 }
262 }
263 }
264 if (mats.Count == 0)
265 return;
266
267 OSDArray matsArr = new OSDArray();
268 foreach (KeyValuePair<UUID, OSDMap> kvp in mats)
269 {
270 OSDMap matOsd = new OSDMap();
271 matOsd["ID"] = OSD.FromUUID(kvp.Key);
272 matOsd["Material"] = kvp.Value;
273 matsArr.Add(matOsd);
274 }
275
276 OSDMap OSMaterials = new OSDMap();
277 OSMaterials["Materials"] = matsArr;
278
279 lock (part.DynAttrs)
280 part.DynAttrs["OS:Materials"] = OSMaterials;
281 }
282 catch (Exception e)
283 {
284 m_log.Warn("[MaterialsDemoModule]: exception in StoreMaterialsForPart(): " + e.ToString());
285 }
286 }
287
288
289 public string RenderMaterialsPostCap(string request, string path,
290 string param, IOSHttpRequest httpRequest,
291 IOSHttpResponse httpResponse)
292 {
293 m_log.Debug("[MaterialsDemoModule]: POST cap handler");
294
295 OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request);
296 OSDMap resp = new OSDMap();
297
298 OSDMap materialsFromViewer = null;
299
300 OSDArray respArr = new OSDArray();
301
302 if (req.ContainsKey("Zipped"))
303 {
304 OSD osd = null;
305
306 byte[] inBytes = req["Zipped"].AsBinary();
307
308 try
309 {
310 osd = ZDecompressBytesToOsd(inBytes);
311
312 if (osd != null)
313 {
314 if (osd is OSDArray) // assume array of MaterialIDs designating requested material entries
315 {
316 foreach (OSD elem in (OSDArray)osd)
317 {
318
319 try
320 {
321 UUID id = new UUID(elem.AsBinary(), 0);
322
323 if (m_knownMaterials.ContainsKey(id))
324 {
325 m_log.Info("[MaterialsDemoModule]: request for known material ID: " + id.ToString());
326 OSDMap matMap = new OSDMap();
327 matMap["ID"] = OSD.FromBinary(id.GetBytes());
328
329 matMap["Material"] = m_knownMaterials[id];
330 respArr.Add(matMap);
331 }
332 else
333 m_log.Info("[MaterialsDemoModule]: request for UNKNOWN material ID: " + id.ToString());
334 }
335 catch (Exception e)
336 {
337 // report something here?
338 continue;
339 }
340 }
341 }
342 else if (osd is OSDMap) // reqest to assign a material
343 {
344 materialsFromViewer = osd as OSDMap;
345
346 if (materialsFromViewer.ContainsKey("FullMaterialsPerFace"))
347 {
348 OSD matsOsd = materialsFromViewer["FullMaterialsPerFace"];
349 if (matsOsd is OSDArray)
350 {
351 OSDArray matsArr = matsOsd as OSDArray;
352
353 try
354 {
355 foreach (OSDMap matsMap in matsArr)
356 {
357 m_log.Debug("[MaterialsDemoModule]: processing matsMap: " + OSDParser.SerializeJsonString(matsMap));
358
359 uint matLocalID = 0;
360 try { matLocalID = matsMap["ID"].AsUInteger(); }
361 catch (Exception e) { m_log.Warn("[MaterialsDemoModule]: cannot decode \"ID\" from matsMap: " + e.Message); }
362 m_log.Debug("[MaterialsDemoModule]: matLocalId: " + matLocalID.ToString());
363
364
365 OSDMap mat = null;
366 try { mat = matsMap["Material"] as OSDMap; }
367 catch (Exception e) { m_log.Warn("[MaterialsDemoModule]: cannot decode \"Material\" from matsMap: " + e.Message); }
368 m_log.Debug("[MaterialsDemoModule]: mat: " + OSDParser.SerializeJsonString(mat));
369
370 UUID id = HashOsd(mat);
371 m_knownMaterials[id] = mat;
372
373
374 var sop = m_scene.GetSceneObjectPart(matLocalID);
375 if (sop == null)
376 m_log.Debug("[MaterialsDemoModule]: null SOP for localId: " + matLocalID.ToString());
377 else
378 {
379 //var te = sop.Shape.Textures;
380 var te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length);
381
382 if (te == null)
383 {
384 m_log.Debug("[MaterialsDemoModule]: null TextureEntry for localId: " + matLocalID.ToString());
385 }
386 else
387 {
388 int face = -1;
389
390 if (matsMap.ContainsKey("Face"))
391 {
392 face = matsMap["Face"].AsInteger();
393 if (te.FaceTextures == null) // && face == 0)
394 {
395 if (te.DefaultTexture == null)
396 m_log.Debug("[MaterialsDemoModule]: te.DefaultTexture is null");
397 else
398 {
399 if (te.DefaultTexture.MaterialID == null)
400 m_log.Debug("[MaterialsDemoModule]: te.DefaultTexture.MaterialID is null");
401 else
402 {
403 te.DefaultTexture.MaterialID = id;
404 }
405 }
406 }
407 else
408 {
409 if (te.FaceTextures.Length >= face - 1)
410 {
411 if (te.FaceTextures[face] == null)
412 te.DefaultTexture.MaterialID = id;
413 else
414 te.FaceTextures[face].MaterialID = id;
415 }
416 }
417 }
418 else
419 {
420 if (te.DefaultTexture != null)
421 te.DefaultTexture.MaterialID = id;
422 }
423
424 m_log.Debug("[MaterialsDemoModule]: setting material ID for face " + face.ToString() + " to " + id.ToString());
425
426 //we cant use sop.UpdateTextureEntry(te); because it filters so do it manually
427
428 if (sop.ParentGroup != null)
429 {
430 sop.Shape.TextureEntry = te.GetBytes();
431 sop.TriggerScriptChangedEvent(Changed.TEXTURE);
432 sop.UpdateFlag = UpdateRequired.FULL;
433 sop.ParentGroup.HasGroupChanged = true;
434
435 sop.ScheduleFullUpdate();
436
437 StoreMaterialsForPart(sop);
438 }
439 }
440 }
441 }
442 }
443 catch (Exception e)
444 {
445 m_log.Warn("[MaterialsDemoModule]: exception processing received material: " + e.Message);
446 }
447 }
448 }
449 }
450 }
451
452 }
453 catch (Exception e)
454 {
455 m_log.Warn("[MaterialsDemoModule]: exception decoding zipped CAP payload: " + e.Message);
456 //return "";
457 }
458 m_log.Debug("[MaterialsDemoModule]: knownMaterials.Count: " + m_knownMaterials.Count.ToString());
459 }
460
461
462 resp["Zipped"] = ZCompressOSD(respArr, false);
463 string response = OSDParser.SerializeLLSDXmlString(resp);
464
465 //m_log.Debug("[MaterialsDemoModule]: cap request: " + request);
466 m_log.Debug("[MaterialsDemoModule]: cap request (zipped portion): " + ZippedOsdBytesToString(req["Zipped"].AsBinary()));
467 m_log.Debug("[MaterialsDemoModule]: cap response: " + response);
468 return response;
469 }
470
471
472 public string RenderMaterialsGetCap(string request, string path,
473 string param, IOSHttpRequest httpRequest,
474 IOSHttpResponse httpResponse)
475 {
476 m_log.Debug("[MaterialsDemoModule]: GET cap handler");
477
478 OSDMap resp = new OSDMap();
479
480
481 int matsCount = 0;
482
483 OSDArray allOsd = new OSDArray();
484
485 foreach (KeyValuePair<UUID, OSDMap> kvp in m_knownMaterials)
486 {
487 OSDMap matMap = new OSDMap();
488
489 matMap["ID"] = OSD.FromBinary(kvp.Key.GetBytes());
490
491 matMap["Material"] = kvp.Value;
492 allOsd.Add(matMap);
493 matsCount++;
494 }
495
496
497 resp["Zipped"] = ZCompressOSD(allOsd, false);
498 m_log.Debug("[MaterialsDemoModule]: matsCount: " + matsCount.ToString());
499
500 return OSDParser.SerializeLLSDXmlString(resp);
501 }
502
503 static string ZippedOsdBytesToString(byte[] bytes)
504 {
505 try
506 {
507 return OSDParser.SerializeJsonString(ZDecompressBytesToOsd(bytes));
508 }
509 catch (Exception e)
510 {
511 return "ZippedOsdBytesToString caught an exception: " + e.ToString();
512 }
513 }
514
515 /// <summary>
516 /// computes a UUID by hashing a OSD object
517 /// </summary>
518 /// <param name="osd"></param>
519 /// <returns></returns>
520 private static UUID HashOsd(OSD osd)
521 {
522 using (var md5 = MD5.Create())
523 using (MemoryStream ms = new MemoryStream(OSDParser.SerializeLLSDBinary(osd, false)))
524 return new UUID(md5.ComputeHash(ms), 0);
525 }
526
527 public static OSD ZCompressOSD(OSD inOsd, bool useHeader)
528 {
529 OSD osd = null;
530
531 using (MemoryStream msSinkCompressed = new MemoryStream())
532 {
533 using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkCompressed,
534 Ionic.Zlib.CompressionMode.Compress, CompressionLevel.BestCompression, true))
535 {
536 CopyStream(new MemoryStream(OSDParser.SerializeLLSDBinary(inOsd, useHeader)), zOut);
537 zOut.Close();
538 }
539
540 msSinkCompressed.Seek(0L, SeekOrigin.Begin);
541 osd = OSD.FromBinary( msSinkCompressed.ToArray());
542 }
543
544 return osd;
545 }
546
547
548 public static OSD ZDecompressBytesToOsd(byte[] input)
549 {
550 OSD osd = null;
551
552 using (MemoryStream msSinkUnCompressed = new MemoryStream())
553 {
554 using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkUnCompressed, CompressionMode.Decompress, true))
555 {
556 CopyStream(new MemoryStream(input), zOut);
557 zOut.Close();
558 }
559 msSinkUnCompressed.Seek(0L, SeekOrigin.Begin);
560 osd = OSDParser.DeserializeLLSDBinary(msSinkUnCompressed.ToArray());
561 }
562
563 return osd;
564 }
565
566 static void CopyStream(System.IO.Stream input, System.IO.Stream output)
567 {
568 byte[] buffer = new byte[2048];
569 int len;
570 while ((len = input.Read(buffer, 0, 2048)) > 0)
571 {
572 output.Write(buffer, 0, len);
573 }
574
575 output.Flush();
576 }
577
578 }
579} \ No newline at end of file