aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules/Materials
diff options
context:
space:
mode:
authorDavid Walter Seikel2016-11-03 21:44:39 +1000
committerDavid Walter Seikel2016-11-03 21:44:39 +1000
commit134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch)
tree216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Region/OptionalModules/Materials
parentMore changing to production grid. Double oops. (diff)
downloadopensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip
opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz
opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2
opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.xz
Initial update to OpenSim 0.8.2.1 source code.
Diffstat (limited to 'OpenSim/Region/OptionalModules/Materials')
-rw-r--r--OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs608
1 files changed, 608 insertions, 0 deletions
diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs
new file mode 100644
index 0000000..e95889d
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs
@@ -0,0 +1,608 @@
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.8.1")]
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 private int m_maxMaterialsPerTransaction = 50;
69
70 public Dictionary<UUID, OSDMap> m_regionMaterials = new Dictionary<UUID, OSDMap>();
71
72 public void Initialise(IConfigSource source)
73 {
74 m_enabled = true; // default is enabled
75
76 IConfig config = source.Configs["Materials"];
77 if (config != null)
78 {
79 m_enabled = config.GetBoolean("enable_materials", m_enabled);
80 m_maxMaterialsPerTransaction = config.GetInt("MaxMaterialsPerTransaction", m_maxMaterialsPerTransaction);
81 }
82
83 if (m_enabled)
84 m_log.DebugFormat("[Materials]: Initialized");
85 }
86
87 public void Close()
88 {
89 if (!m_enabled)
90 return;
91 }
92
93 public void AddRegion(Scene scene)
94 {
95 if (!m_enabled)
96 return;
97
98 m_scene = scene;
99 m_scene.EventManager.OnRegisterCaps += OnRegisterCaps;
100 m_scene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene;
101 }
102
103 private void EventManager_OnObjectAddedToScene(SceneObjectGroup obj)
104 {
105 foreach (var part in obj.Parts)
106 if (part != null)
107 GetStoredMaterialsInPart(part);
108 }
109
110 private void OnRegisterCaps(OpenMetaverse.UUID agentID, OpenSim.Framework.Capabilities.Caps caps)
111 {
112 string capsBase = "/CAPS/" + caps.CapsObjectPath;
113
114 IRequestHandler renderMaterialsPostHandler
115 = new RestStreamHandler("POST", capsBase + "/",
116 (request, path, param, httpRequest, httpResponse)
117 => RenderMaterialsPostCap(request, agentID),
118 "RenderMaterials", null);
119 caps.RegisterHandler("RenderMaterials", renderMaterialsPostHandler);
120
121 // OpenSimulator CAPs infrastructure seems to be somewhat hostile towards any CAP that requires both GET
122 // and POST handlers, (at least at the time this was originally written), so we first set up a POST
123 // handler normally and then add a GET handler via MainServer
124
125 IRequestHandler renderMaterialsGetHandler
126 = new RestStreamHandler("GET", capsBase + "/",
127 (request, path, param, httpRequest, httpResponse)
128 => RenderMaterialsGetCap(request),
129 "RenderMaterials", null);
130 MainServer.Instance.AddStreamHandler(renderMaterialsGetHandler);
131
132 // materials viewer seems to use either POST or PUT, so assign POST handler for PUT as well
133 IRequestHandler renderMaterialsPutHandler
134 = new RestStreamHandler("PUT", capsBase + "/",
135 (request, path, param, httpRequest, httpResponse)
136 => RenderMaterialsPostCap(request, agentID),
137 "RenderMaterials", null);
138 MainServer.Instance.AddStreamHandler(renderMaterialsPutHandler);
139 }
140
141 public void RemoveRegion(Scene scene)
142 {
143 if (!m_enabled)
144 return;
145
146 m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps;
147 m_scene.EventManager.OnObjectAddedToScene -= EventManager_OnObjectAddedToScene;
148 }
149
150 public void RegionLoaded(Scene scene)
151 {
152 if (!m_enabled) return;
153
154 ISimulatorFeaturesModule featuresModule = scene.RequestModuleInterface<ISimulatorFeaturesModule>();
155 if (featuresModule != null)
156 featuresModule.OnSimulatorFeaturesRequest += OnSimulatorFeaturesRequest;
157 }
158
159 private void OnSimulatorFeaturesRequest(UUID agentID, ref OSDMap features)
160 {
161 features["MaxMaterialsPerTransaction"] = m_maxMaterialsPerTransaction;
162 }
163
164 /// <summary>
165 /// Finds any legacy materials stored in DynAttrs that may exist for this part and add them to 'm_regionMaterials'.
166 /// </summary>
167 /// <param name="part"></param>
168 private void GetLegacyStoredMaterialsInPart(SceneObjectPart part)
169 {
170 if (part.DynAttrs == null)
171 return;
172
173 OSD OSMaterials = null;
174 OSDArray matsArr = null;
175
176 lock (part.DynAttrs)
177 {
178 if (part.DynAttrs.ContainsStore("OpenSim", "Materials"))
179 {
180 OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials");
181
182 if (materialsStore == null)
183 return;
184
185 materialsStore.TryGetValue("Materials", out OSMaterials);
186 }
187
188 if (OSMaterials != null && OSMaterials is OSDArray)
189 matsArr = OSMaterials as OSDArray;
190 else
191 return;
192 }
193
194 if (matsArr == null)
195 return;
196
197 foreach (OSD elemOsd in matsArr)
198 {
199 if (elemOsd != null && elemOsd is OSDMap)
200 {
201 OSDMap matMap = elemOsd as OSDMap;
202 if (matMap.ContainsKey("ID") && matMap.ContainsKey("Material"))
203 {
204 try
205 {
206 lock (m_regionMaterials)
207 m_regionMaterials[matMap["ID"].AsUUID()] = (OSDMap)matMap["Material"];
208 }
209 catch (Exception e)
210 {
211 m_log.Warn("[Materials]: exception decoding persisted legacy material: " + e.ToString());
212 }
213 }
214 }
215 }
216 }
217
218 /// <summary>
219 /// Find the materials used in the SOP, and add them to 'm_regionMaterials'.
220 /// </summary>
221 private void GetStoredMaterialsInPart(SceneObjectPart part)
222 {
223 if (part.Shape == null)
224 return;
225
226 var te = new Primitive.TextureEntry(part.Shape.TextureEntry, 0, part.Shape.TextureEntry.Length);
227 if (te == null)
228 return;
229
230 GetLegacyStoredMaterialsInPart(part);
231
232 if (te.DefaultTexture != null)
233 GetStoredMaterialInFace(part, te.DefaultTexture);
234 else
235 m_log.WarnFormat(
236 "[Materials]: Default texture for part {0} (part of object {1}) in {2} unexpectedly null. Ignoring.",
237 part.Name, part.ParentGroup.Name, m_scene.Name);
238
239 foreach (Primitive.TextureEntryFace face in te.FaceTextures)
240 {
241 if (face != null)
242 GetStoredMaterialInFace(part, face);
243 }
244 }
245
246 /// <summary>
247 /// Find the materials used in one Face, and add them to 'm_regionMaterials'.
248 /// </summary>
249 private void GetStoredMaterialInFace(SceneObjectPart part, Primitive.TextureEntryFace face)
250 {
251 UUID id = face.MaterialID;
252 if (id == UUID.Zero)
253 return;
254
255 lock (m_regionMaterials)
256 {
257 if (m_regionMaterials.ContainsKey(id))
258 return;
259
260 byte[] data = m_scene.AssetService.GetData(id.ToString());
261 if (data == null)
262 {
263 m_log.WarnFormat("[Materials]: Prim \"{0}\" ({1}) contains unknown material ID {2}", part.Name, part.UUID, id);
264 return;
265 }
266
267 OSDMap mat;
268 try
269 {
270 mat = (OSDMap)OSDParser.DeserializeLLSDXml(data);
271 }
272 catch (Exception e)
273 {
274 m_log.WarnFormat("[Materials]: cannot decode material asset {0}: {1}", id, e.Message);
275 return;
276 }
277
278 m_regionMaterials[id] = mat;
279 }
280 }
281
282 public string RenderMaterialsPostCap(string request, UUID agentID)
283 {
284 OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request);
285 OSDMap resp = new OSDMap();
286
287 OSDMap materialsFromViewer = null;
288
289 OSDArray respArr = new OSDArray();
290
291 if (req.ContainsKey("Zipped"))
292 {
293 OSD osd = null;
294
295 byte[] inBytes = req["Zipped"].AsBinary();
296
297 try
298 {
299 osd = ZDecompressBytesToOsd(inBytes);
300
301 if (osd != null)
302 {
303 if (osd is OSDArray) // assume array of MaterialIDs designating requested material entries
304 {
305 foreach (OSD elem in (OSDArray)osd)
306 {
307 try
308 {
309 UUID id = new UUID(elem.AsBinary(), 0);
310
311 lock (m_regionMaterials)
312 {
313 if (m_regionMaterials.ContainsKey(id))
314 {
315 OSDMap matMap = new OSDMap();
316 matMap["ID"] = OSD.FromBinary(id.GetBytes());
317 matMap["Material"] = m_regionMaterials[id];
318 respArr.Add(matMap);
319 }
320 else
321 {
322 m_log.Warn("[Materials]: request for unknown material ID: " + id.ToString());
323
324 // Theoretically we could try to load the material from the assets service,
325 // but that shouldn't be necessary because the viewer should only request
326 // materials that exist in a prim on the region, and all of these materials
327 // are already stored in m_regionMaterials.
328 }
329 }
330 }
331 catch (Exception e)
332 {
333 m_log.Error("Error getting materials in response to viewer request", e);
334 continue;
335 }
336 }
337 }
338 else if (osd is OSDMap) // request to assign a material
339 {
340 materialsFromViewer = osd as OSDMap;
341
342 if (materialsFromViewer.ContainsKey("FullMaterialsPerFace"))
343 {
344 OSD matsOsd = materialsFromViewer["FullMaterialsPerFace"];
345 if (matsOsd is OSDArray)
346 {
347 OSDArray matsArr = matsOsd as OSDArray;
348
349 try
350 {
351 foreach (OSDMap matsMap in matsArr)
352 {
353 uint primLocalID = 0;
354 try {
355 primLocalID = matsMap["ID"].AsUInteger();
356 }
357 catch (Exception e) {
358 m_log.Warn("[Materials]: cannot decode \"ID\" from matsMap: " + e.Message);
359 continue;
360 }
361
362 OSDMap mat = null;
363 try
364 {
365 mat = matsMap["Material"] as OSDMap;
366 }
367 catch (Exception e)
368 {
369 m_log.Warn("[Materials]: cannot decode \"Material\" from matsMap: " + e.Message);
370 continue;
371 }
372
373 SceneObjectPart sop = m_scene.GetSceneObjectPart(primLocalID);
374 if (sop == null)
375 {
376 m_log.WarnFormat("[Materials]: SOP not found for localId: {0}", primLocalID.ToString());
377 continue;
378 }
379
380 if (!m_scene.Permissions.CanEditObject(sop.UUID, agentID))
381 {
382 m_log.WarnFormat("User {0} can't edit object {1} {2}", agentID, sop.Name, sop.UUID);
383 continue;
384 }
385
386 Primitive.TextureEntry te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length);
387 if (te == null)
388 {
389 m_log.WarnFormat("[Materials]: Error in TextureEntry for SOP {0} {1}", sop.Name, sop.UUID);
390 continue;
391 }
392
393
394 UUID id;
395 if (mat == null)
396 {
397 // This happens then the user removes a material from a prim
398 id = UUID.Zero;
399 }
400 else
401 {
402 id = StoreMaterialAsAsset(agentID, mat, sop);
403 }
404
405
406 int face = -1;
407
408 if (matsMap.ContainsKey("Face"))
409 {
410 face = matsMap["Face"].AsInteger();
411 Primitive.TextureEntryFace faceEntry = te.CreateFace((uint)face);
412 faceEntry.MaterialID = id;
413 }
414 else
415 {
416 if (te.DefaultTexture == null)
417 m_log.WarnFormat("[Materials]: TextureEntry.DefaultTexture is null in {0} {1}", sop.Name, sop.UUID);
418 else
419 te.DefaultTexture.MaterialID = id;
420 }
421
422 //m_log.DebugFormat("[Materials]: in \"{0}\" {1}, setting material ID for face {2} to {3}", sop.Name, sop.UUID, face, id);
423
424 // We can't use sop.UpdateTextureEntry(te) because it filters, so do it manually
425 sop.Shape.TextureEntry = te.GetBytes();
426
427 if (sop.ParentGroup != null)
428 {
429 sop.TriggerScriptChangedEvent(Changed.TEXTURE);
430 sop.UpdateFlag = UpdateRequired.FULL;
431 sop.ParentGroup.HasGroupChanged = true;
432 sop.ScheduleFullUpdate();
433 }
434 }
435 }
436 catch (Exception e)
437 {
438 m_log.Warn("[Materials]: exception processing received material ", e);
439 }
440 }
441 }
442 }
443 }
444
445 }
446 catch (Exception e)
447 {
448 m_log.Warn("[Materials]: exception decoding zipped CAP payload ", e);
449 //return "";
450 }
451 }
452
453
454 resp["Zipped"] = ZCompressOSD(respArr, false);
455 string response = OSDParser.SerializeLLSDXmlString(resp);
456
457 //m_log.Debug("[Materials]: cap request: " + request);
458 //m_log.Debug("[Materials]: cap request (zipped portion): " + ZippedOsdBytesToString(req["Zipped"].AsBinary()));
459 //m_log.Debug("[Materials]: cap response: " + response);
460 return response;
461 }
462
463 private UUID StoreMaterialAsAsset(UUID agentID, OSDMap mat, SceneObjectPart sop)
464 {
465 UUID id;
466 // Material UUID = hash of the material's data.
467 // This makes materials deduplicate across the entire grid (but isn't otherwise required).
468 byte[] data = System.Text.Encoding.ASCII.GetBytes(OSDParser.SerializeLLSDXmlString(mat));
469 using (var md5 = MD5.Create())
470 id = new UUID(md5.ComputeHash(data), 0);
471
472 lock (m_regionMaterials)
473 {
474 if (!m_regionMaterials.ContainsKey(id))
475 {
476 m_regionMaterials[id] = mat;
477
478 // This asset might exist already, but it's ok to try to store it again
479 string name = "Material " + ChooseMaterialName(mat, sop);
480 name = name.Substring(0, Math.Min(64, name.Length)).Trim();
481 AssetBase asset = new AssetBase(id, name, (sbyte)OpenSimAssetType.Material, agentID.ToString());
482 asset.Data = data;
483 m_scene.AssetService.Store(asset);
484 }
485 }
486 return id;
487 }
488
489 /// <summary>
490 /// Use heuristics to choose a good name for the material.
491 /// </summary>
492 private string ChooseMaterialName(OSDMap mat, SceneObjectPart sop)
493 {
494 UUID normMap = mat["NormMap"].AsUUID();
495 if (normMap != UUID.Zero)
496 {
497 AssetBase asset = m_scene.AssetService.GetCached(normMap.ToString());
498 if ((asset != null) && (asset.Name.Length > 0) && !asset.Name.Equals("From IAR"))
499 return asset.Name;
500 }
501
502 UUID specMap = mat["SpecMap"].AsUUID();
503 if (specMap != UUID.Zero)
504 {
505 AssetBase asset = m_scene.AssetService.GetCached(specMap.ToString());
506 if ((asset != null) && (asset.Name.Length > 0) && !asset.Name.Equals("From IAR"))
507 return asset.Name;
508 }
509
510 if (sop.Name != "Primitive")
511 return sop.Name;
512
513 if ((sop.ParentGroup != null) && (sop.ParentGroup.Name != "Primitive"))
514 return sop.ParentGroup.Name;
515
516 return "";
517 }
518
519
520 public string RenderMaterialsGetCap(string request)
521 {
522 OSDMap resp = new OSDMap();
523 int matsCount = 0;
524 OSDArray allOsd = new OSDArray();
525
526 lock (m_regionMaterials)
527 {
528 foreach (KeyValuePair<UUID, OSDMap> kvp in m_regionMaterials)
529 {
530 OSDMap matMap = new OSDMap();
531
532 matMap["ID"] = OSD.FromBinary(kvp.Key.GetBytes());
533 matMap["Material"] = kvp.Value;
534 allOsd.Add(matMap);
535 matsCount++;
536 }
537 }
538
539 resp["Zipped"] = ZCompressOSD(allOsd, false);
540
541 return OSDParser.SerializeLLSDXmlString(resp);
542 }
543
544 private static string ZippedOsdBytesToString(byte[] bytes)
545 {
546 try
547 {
548 return OSDParser.SerializeJsonString(ZDecompressBytesToOsd(bytes));
549 }
550 catch (Exception e)
551 {
552 return "ZippedOsdBytesToString caught an exception: " + e.ToString();
553 }
554 }
555
556 /// <summary>
557 /// computes a UUID by hashing a OSD object
558 /// </summary>
559 /// <param name="osd"></param>
560 /// <returns></returns>
561 private static UUID HashOsd(OSD osd)
562 {
563 byte[] data = OSDParser.SerializeLLSDBinary(osd, false);
564 using (var md5 = MD5.Create())
565 return new UUID(md5.ComputeHash(data), 0);
566 }
567
568 public static OSD ZCompressOSD(OSD inOsd, bool useHeader)
569 {
570 OSD osd = null;
571
572 byte[] data = OSDParser.SerializeLLSDBinary(inOsd, useHeader);
573
574 using (MemoryStream msSinkCompressed = new MemoryStream())
575 {
576 using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkCompressed,
577 Ionic.Zlib.CompressionMode.Compress, CompressionLevel.BestCompression, true))
578 {
579 zOut.Write(data, 0, data.Length);
580 }
581
582 msSinkCompressed.Seek(0L, SeekOrigin.Begin);
583 osd = OSD.FromBinary(msSinkCompressed.ToArray());
584 }
585
586 return osd;
587 }
588
589
590 public static OSD ZDecompressBytesToOsd(byte[] input)
591 {
592 OSD osd = null;
593
594 using (MemoryStream msSinkUnCompressed = new MemoryStream())
595 {
596 using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkUnCompressed, CompressionMode.Decompress, true))
597 {
598 zOut.Write(input, 0, input.Length);
599 }
600
601 msSinkUnCompressed.Seek(0L, SeekOrigin.Begin);
602 osd = OSDParser.DeserializeLLSDBinary(msSinkUnCompressed.ToArray());
603 }
604
605 return osd;
606 }
607 }
608}