diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs | 453 |
1 files changed, 314 insertions, 139 deletions
diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index e95889d..e8cb052 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs | |||
@@ -59,15 +59,20 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
59 | { | 59 | { |
60 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 60 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
61 | 61 | ||
62 | public string Name { get { return "MaterialsModule"; } } | 62 | public string Name { get { return "MaterialsModule"; } } |
63 | 63 | ||
64 | public Type ReplaceableInterface { get { return null; } } | 64 | public Type ReplaceableInterface { get { return null; } } |
65 | 65 | ||
66 | IAssetCache m_cache; | ||
66 | private Scene m_scene = null; | 67 | private Scene m_scene = null; |
67 | private bool m_enabled = false; | 68 | private bool m_enabled = false; |
68 | private int m_maxMaterialsPerTransaction = 50; | 69 | private int m_maxMaterialsPerTransaction = 50; |
69 | 70 | ||
70 | public Dictionary<UUID, OSDMap> m_regionMaterials = new Dictionary<UUID, OSDMap>(); | 71 | public Dictionary<UUID, OSDMap> m_Materials = new Dictionary<UUID, OSDMap>(); |
72 | public Dictionary<UUID, int> m_MaterialsRefCount = new Dictionary<UUID, int>(); | ||
73 | |||
74 | private Dictionary<ulong, AssetBase> m_changes = new Dictionary<ulong, AssetBase>(); | ||
75 | private Dictionary<ulong, double> m_changesTime = new Dictionary<ulong, double>(); | ||
71 | 76 | ||
72 | public void Initialise(IConfigSource source) | 77 | public void Initialise(IConfigSource source) |
73 | { | 78 | { |
@@ -83,13 +88,13 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
83 | if (m_enabled) | 88 | if (m_enabled) |
84 | m_log.DebugFormat("[Materials]: Initialized"); | 89 | m_log.DebugFormat("[Materials]: Initialized"); |
85 | } | 90 | } |
86 | 91 | ||
87 | public void Close() | 92 | public void Close() |
88 | { | 93 | { |
89 | if (!m_enabled) | 94 | if (!m_enabled) |
90 | return; | 95 | return; |
91 | } | 96 | } |
92 | 97 | ||
93 | public void AddRegion(Scene scene) | 98 | public void AddRegion(Scene scene) |
94 | { | 99 | { |
95 | if (!m_enabled) | 100 | if (!m_enabled) |
@@ -98,6 +103,56 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
98 | m_scene = scene; | 103 | m_scene = scene; |
99 | m_scene.EventManager.OnRegisterCaps += OnRegisterCaps; | 104 | m_scene.EventManager.OnRegisterCaps += OnRegisterCaps; |
100 | m_scene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene; | 105 | m_scene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene; |
106 | m_scene.EventManager.OnBackup += EventManager_OnBackup; | ||
107 | } | ||
108 | |||
109 | private void EventManager_OnBackup(ISimulationDataService datastore, bool forcedBackup) | ||
110 | { | ||
111 | List<AssetBase> toStore; | ||
112 | List<ulong> hashlist; | ||
113 | |||
114 | |||
115 | lock (m_Materials) | ||
116 | { | ||
117 | if(m_changes.Count == 0) | ||
118 | return; | ||
119 | |||
120 | if(forcedBackup) | ||
121 | { | ||
122 | toStore = new List<AssetBase>(m_changes.Values); | ||
123 | m_changes.Clear(); | ||
124 | m_changesTime.Clear(); | ||
125 | } | ||
126 | else | ||
127 | { | ||
128 | toStore = new List<AssetBase>(); | ||
129 | hashlist = new List<ulong>(); | ||
130 | double storetime = Util.GetTimeStampMS() - 60000; | ||
131 | foreach(KeyValuePair<ulong,double> kvp in m_changesTime) | ||
132 | { | ||
133 | if(kvp.Value < storetime) | ||
134 | { | ||
135 | toStore.Add(m_changes[kvp.Key]); | ||
136 | hashlist.Add(kvp.Key); | ||
137 | } | ||
138 | } | ||
139 | foreach(ulong u in hashlist) | ||
140 | { | ||
141 | m_changesTime.Remove(u); | ||
142 | m_changes.Remove(u); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | if(toStore.Count > 0) | ||
147 | Util.FireAndForget(delegate | ||
148 | { | ||
149 | foreach(AssetBase a in toStore) | ||
150 | { | ||
151 | a.Local = false; | ||
152 | m_scene.AssetService.Store(a); | ||
153 | } | ||
154 | }); | ||
155 | } | ||
101 | } | 156 | } |
102 | 157 | ||
103 | private void EventManager_OnObjectAddedToScene(SceneObjectGroup obj) | 158 | private void EventManager_OnObjectAddedToScene(SceneObjectGroup obj) |
@@ -111,7 +166,7 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
111 | { | 166 | { |
112 | string capsBase = "/CAPS/" + caps.CapsObjectPath; | 167 | string capsBase = "/CAPS/" + caps.CapsObjectPath; |
113 | 168 | ||
114 | IRequestHandler renderMaterialsPostHandler | 169 | IRequestHandler renderMaterialsPostHandler |
115 | = new RestStreamHandler("POST", capsBase + "/", | 170 | = new RestStreamHandler("POST", capsBase + "/", |
116 | (request, path, param, httpRequest, httpResponse) | 171 | (request, path, param, httpRequest, httpResponse) |
117 | => RenderMaterialsPostCap(request, agentID), | 172 | => RenderMaterialsPostCap(request, agentID), |
@@ -122,7 +177,7 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
122 | // and POST handlers, (at least at the time this was originally written), so we first set up a POST | 177 | // 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 | 178 | // handler normally and then add a GET handler via MainServer |
124 | 179 | ||
125 | IRequestHandler renderMaterialsGetHandler | 180 | IRequestHandler renderMaterialsGetHandler |
126 | = new RestStreamHandler("GET", capsBase + "/", | 181 | = new RestStreamHandler("GET", capsBase + "/", |
127 | (request, path, param, httpRequest, httpResponse) | 182 | (request, path, param, httpRequest, httpResponse) |
128 | => RenderMaterialsGetCap(request), | 183 | => RenderMaterialsGetCap(request), |
@@ -130,14 +185,14 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
130 | MainServer.Instance.AddStreamHandler(renderMaterialsGetHandler); | 185 | MainServer.Instance.AddStreamHandler(renderMaterialsGetHandler); |
131 | 186 | ||
132 | // materials viewer seems to use either POST or PUT, so assign POST handler for PUT as well | 187 | // materials viewer seems to use either POST or PUT, so assign POST handler for PUT as well |
133 | IRequestHandler renderMaterialsPutHandler | 188 | IRequestHandler renderMaterialsPutHandler |
134 | = new RestStreamHandler("PUT", capsBase + "/", | 189 | = new RestStreamHandler("PUT", capsBase + "/", |
135 | (request, path, param, httpRequest, httpResponse) | 190 | (request, path, param, httpRequest, httpResponse) |
136 | => RenderMaterialsPostCap(request, agentID), | 191 | => RenderMaterialsPutCap(request, agentID), |
137 | "RenderMaterials", null); | 192 | "RenderMaterials", null); |
138 | MainServer.Instance.AddStreamHandler(renderMaterialsPutHandler); | 193 | MainServer.Instance.AddStreamHandler(renderMaterialsPutHandler); |
139 | } | 194 | } |
140 | 195 | ||
141 | public void RemoveRegion(Scene scene) | 196 | public void RemoveRegion(Scene scene) |
142 | { | 197 | { |
143 | if (!m_enabled) | 198 | if (!m_enabled) |
@@ -145,12 +200,14 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
145 | 200 | ||
146 | m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps; | 201 | m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps; |
147 | m_scene.EventManager.OnObjectAddedToScene -= EventManager_OnObjectAddedToScene; | 202 | m_scene.EventManager.OnObjectAddedToScene -= EventManager_OnObjectAddedToScene; |
148 | } | 203 | m_scene.EventManager.OnBackup -= EventManager_OnBackup; |
149 | 204 | } | |
205 | |||
150 | public void RegionLoaded(Scene scene) | 206 | public void RegionLoaded(Scene scene) |
151 | { | 207 | { |
152 | if (!m_enabled) return; | 208 | if (!m_enabled) return; |
153 | 209 | ||
210 | m_cache = scene.RequestModuleInterface<IAssetCache>(); | ||
154 | ISimulatorFeaturesModule featuresModule = scene.RequestModuleInterface<ISimulatorFeaturesModule>(); | 211 | ISimulatorFeaturesModule featuresModule = scene.RequestModuleInterface<ISimulatorFeaturesModule>(); |
155 | if (featuresModule != null) | 212 | if (featuresModule != null) |
156 | featuresModule.OnSimulatorFeaturesRequest += OnSimulatorFeaturesRequest; | 213 | featuresModule.OnSimulatorFeaturesRequest += OnSimulatorFeaturesRequest; |
@@ -203,8 +260,17 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
203 | { | 260 | { |
204 | try | 261 | try |
205 | { | 262 | { |
206 | lock (m_regionMaterials) | 263 | lock (m_Materials) |
207 | m_regionMaterials[matMap["ID"].AsUUID()] = (OSDMap)matMap["Material"]; | 264 | { |
265 | UUID id = matMap["ID"].AsUUID(); | ||
266 | if(m_Materials.ContainsKey(id)) | ||
267 | m_MaterialsRefCount[id]++; | ||
268 | else | ||
269 | { | ||
270 | m_Materials[id] = (OSDMap)matMap["Material"]; | ||
271 | m_MaterialsRefCount[id] = 1; | ||
272 | } | ||
273 | } | ||
208 | } | 274 | } |
209 | catch (Exception e) | 275 | catch (Exception e) |
210 | { | 276 | { |
@@ -219,7 +285,7 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
219 | /// Find the materials used in the SOP, and add them to 'm_regionMaterials'. | 285 | /// Find the materials used in the SOP, and add them to 'm_regionMaterials'. |
220 | /// </summary> | 286 | /// </summary> |
221 | private void GetStoredMaterialsInPart(SceneObjectPart part) | 287 | private void GetStoredMaterialsInPart(SceneObjectPart part) |
222 | { | 288 | { |
223 | if (part.Shape == null) | 289 | if (part.Shape == null) |
224 | return; | 290 | return; |
225 | 291 | ||
@@ -233,7 +299,7 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
233 | GetStoredMaterialInFace(part, te.DefaultTexture); | 299 | GetStoredMaterialInFace(part, te.DefaultTexture); |
234 | else | 300 | else |
235 | m_log.WarnFormat( | 301 | m_log.WarnFormat( |
236 | "[Materials]: Default texture for part {0} (part of object {1}) in {2} unexpectedly null. Ignoring.", | 302 | "[Materials]: Default texture for part {0} (part of object {1}) in {2} unexpectedly null. Ignoring.", |
237 | part.Name, part.ParentGroup.Name, m_scene.Name); | 303 | part.Name, part.ParentGroup.Name, m_scene.Name); |
238 | 304 | ||
239 | foreach (Primitive.TextureEntryFace face in te.FaceTextures) | 305 | foreach (Primitive.TextureEntryFace face in te.FaceTextures) |
@@ -252,18 +318,22 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
252 | if (id == UUID.Zero) | 318 | if (id == UUID.Zero) |
253 | return; | 319 | return; |
254 | 320 | ||
255 | lock (m_regionMaterials) | 321 | lock (m_Materials) |
256 | { | 322 | { |
257 | if (m_regionMaterials.ContainsKey(id)) | 323 | if (m_Materials.ContainsKey(id)) |
324 | { | ||
325 | m_MaterialsRefCount[id]++; | ||
258 | return; | 326 | return; |
259 | 327 | } | |
260 | byte[] data = m_scene.AssetService.GetData(id.ToString()); | 328 | |
261 | if (data == null) | 329 | AssetBase matAsset = m_scene.AssetService.Get(id.ToString()); |
330 | if (matAsset == null || matAsset.Data == null || matAsset.Data.Length == 0 ) | ||
262 | { | 331 | { |
263 | m_log.WarnFormat("[Materials]: Prim \"{0}\" ({1}) contains unknown material ID {2}", part.Name, part.UUID, id); | 332 | //m_log.WarnFormat("[Materials]: Prim \"{0}\" ({1}) contains unknown material ID {2}", part.Name, part.UUID, id); |
264 | return; | 333 | return; |
265 | } | 334 | } |
266 | 335 | ||
336 | byte[] data = matAsset.Data; | ||
267 | OSDMap mat; | 337 | OSDMap mat; |
268 | try | 338 | try |
269 | { | 339 | { |
@@ -275,7 +345,8 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
275 | return; | 345 | return; |
276 | } | 346 | } |
277 | 347 | ||
278 | m_regionMaterials[id] = mat; | 348 | m_Materials[id] = mat; |
349 | m_MaterialsRefCount[id] = 1; | ||
279 | } | 350 | } |
280 | } | 351 | } |
281 | 352 | ||
@@ -284,8 +355,6 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
284 | OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); | 355 | OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); |
285 | OSDMap resp = new OSDMap(); | 356 | OSDMap resp = new OSDMap(); |
286 | 357 | ||
287 | OSDMap materialsFromViewer = null; | ||
288 | |||
289 | OSDArray respArr = new OSDArray(); | 358 | OSDArray respArr = new OSDArray(); |
290 | 359 | ||
291 | if (req.ContainsKey("Zipped")) | 360 | if (req.ContainsKey("Zipped")) |
@@ -294,154 +363,224 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
294 | 363 | ||
295 | byte[] inBytes = req["Zipped"].AsBinary(); | 364 | byte[] inBytes = req["Zipped"].AsBinary(); |
296 | 365 | ||
297 | try | 366 | try |
298 | { | 367 | { |
299 | osd = ZDecompressBytesToOsd(inBytes); | 368 | osd = ZDecompressBytesToOsd(inBytes); |
300 | 369 | ||
301 | if (osd != null) | 370 | if (osd != null && osd is OSDArray) |
302 | { | 371 | { |
303 | if (osd is OSDArray) // assume array of MaterialIDs designating requested material entries | 372 | foreach (OSD elem in (OSDArray)osd) |
304 | { | 373 | { |
305 | foreach (OSD elem in (OSDArray)osd) | 374 | try |
306 | { | 375 | { |
307 | try | 376 | UUID id = new UUID(elem.AsBinary(), 0); |
308 | { | ||
309 | UUID id = new UUID(elem.AsBinary(), 0); | ||
310 | 377 | ||
311 | lock (m_regionMaterials) | 378 | lock (m_Materials) |
379 | { | ||
380 | if (m_Materials.ContainsKey(id)) | ||
312 | { | 381 | { |
313 | if (m_regionMaterials.ContainsKey(id)) | 382 | OSDMap matMap = new OSDMap(); |
314 | { | 383 | matMap["ID"] = OSD.FromBinary(id.GetBytes()); |
315 | OSDMap matMap = new OSDMap(); | 384 | matMap["Material"] = m_Materials[id]; |
316 | matMap["ID"] = OSD.FromBinary(id.GetBytes()); | 385 | respArr.Add(matMap); |
317 | matMap["Material"] = m_regionMaterials[id]; | 386 | } |
318 | respArr.Add(matMap); | 387 | else |
319 | } | 388 | { |
320 | else | 389 | m_log.Warn("[Materials]: request for unknown material ID: " + id.ToString()); |
321 | { | ||
322 | m_log.Warn("[Materials]: request for unknown material ID: " + id.ToString()); | ||
323 | 390 | ||
324 | // Theoretically we could try to load the material from the assets service, | 391 | // 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 | 392 | // 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 | 393 | // materials that exist in a prim on the region, and all of these materials |
327 | // are already stored in m_regionMaterials. | 394 | // are already stored in m_regionMaterials. |
328 | } | ||
329 | } | 395 | } |
330 | } | 396 | } |
331 | catch (Exception e) | 397 | } |
332 | { | 398 | catch (Exception e) |
333 | m_log.Error("Error getting materials in response to viewer request", e); | 399 | { |
334 | continue; | 400 | m_log.Error("Error getting materials in response to viewer request", e); |
335 | } | 401 | continue; |
336 | } | 402 | } |
337 | } | 403 | } |
338 | else if (osd is OSDMap) // request to assign a material | 404 | } |
339 | { | 405 | } |
340 | materialsFromViewer = osd as OSDMap; | 406 | catch (Exception e) |
407 | { | ||
408 | m_log.Warn("[Materials]: exception decoding zipped CAP payload ", e); | ||
409 | //return ""; | ||
410 | } | ||
411 | } | ||
412 | |||
413 | resp["Zipped"] = ZCompressOSD(respArr, false); | ||
414 | string response = OSDParser.SerializeLLSDXmlString(resp); | ||
415 | |||
416 | //m_log.Debug("[Materials]: cap request: " + request); | ||
417 | //m_log.Debug("[Materials]: cap request (zipped portion): " + ZippedOsdBytesToString(req["Zipped"].AsBinary())); | ||
418 | //m_log.Debug("[Materials]: cap response: " + response); | ||
419 | return response; | ||
420 | } | ||
421 | |||
422 | public string RenderMaterialsPutCap(string request, UUID agentID) | ||
423 | { | ||
424 | OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); | ||
425 | OSDMap resp = new OSDMap(); | ||
426 | |||
427 | OSDMap materialsFromViewer = null; | ||
428 | |||
429 | OSDArray respArr = new OSDArray(); | ||
430 | |||
431 | HashSet<SceneObjectPart> parts = new HashSet<SceneObjectPart>(); | ||
432 | if (req.ContainsKey("Zipped")) | ||
433 | { | ||
434 | OSD osd = null; | ||
435 | |||
436 | byte[] inBytes = req["Zipped"].AsBinary(); | ||
341 | 437 | ||
342 | if (materialsFromViewer.ContainsKey("FullMaterialsPerFace")) | 438 | try |
439 | { | ||
440 | osd = ZDecompressBytesToOsd(inBytes); | ||
441 | |||
442 | if (osd != null && osd is OSDMap) | ||
443 | { | ||
444 | materialsFromViewer = osd as OSDMap; | ||
445 | |||
446 | if (materialsFromViewer.ContainsKey("FullMaterialsPerFace")) | ||
447 | { | ||
448 | OSD matsOsd = materialsFromViewer["FullMaterialsPerFace"]; | ||
449 | if (matsOsd is OSDArray) | ||
343 | { | 450 | { |
344 | OSD matsOsd = materialsFromViewer["FullMaterialsPerFace"]; | 451 | OSDArray matsArr = matsOsd as OSDArray; |
345 | if (matsOsd is OSDArray) | ||
346 | { | ||
347 | OSDArray matsArr = matsOsd as OSDArray; | ||
348 | 452 | ||
349 | try | 453 | try |
454 | { | ||
455 | foreach (OSDMap matsMap in matsArr) | ||
350 | { | 456 | { |
351 | foreach (OSDMap matsMap in matsArr) | 457 | uint primLocalID = 0; |
352 | { | 458 | try { |
353 | uint primLocalID = 0; | 459 | primLocalID = matsMap["ID"].AsUInteger(); |
354 | try { | 460 | } |
355 | primLocalID = matsMap["ID"].AsUInteger(); | 461 | catch (Exception e) { |
356 | } | 462 | m_log.Warn("[Materials]: cannot decode \"ID\" from matsMap: " + e.Message); |
357 | catch (Exception e) { | 463 | continue; |
358 | m_log.Warn("[Materials]: cannot decode \"ID\" from matsMap: " + e.Message); | 464 | } |
359 | continue; | ||
360 | } | ||
361 | 465 | ||
362 | OSDMap mat = null; | 466 | SceneObjectPart sop = m_scene.GetSceneObjectPart(primLocalID); |
363 | try | 467 | if (sop == null) |
364 | { | 468 | { |
365 | mat = matsMap["Material"] as OSDMap; | 469 | m_log.WarnFormat("[Materials]: SOP not found for localId: {0}", primLocalID.ToString()); |
366 | } | 470 | continue; |
367 | catch (Exception e) | 471 | } |
368 | { | ||
369 | m_log.Warn("[Materials]: cannot decode \"Material\" from matsMap: " + e.Message); | ||
370 | continue; | ||
371 | } | ||
372 | 472 | ||
373 | SceneObjectPart sop = m_scene.GetSceneObjectPart(primLocalID); | 473 | if (!m_scene.Permissions.CanEditObject(sop.UUID, agentID)) |
374 | if (sop == null) | 474 | { |
375 | { | 475 | m_log.WarnFormat("User {0} can't edit object {1} {2}", agentID, sop.Name, sop.UUID); |
376 | m_log.WarnFormat("[Materials]: SOP not found for localId: {0}", primLocalID.ToString()); | 476 | continue; |
377 | continue; | 477 | } |
378 | } | ||
379 | 478 | ||
380 | if (!m_scene.Permissions.CanEditObject(sop.UUID, agentID)) | 479 | OSDMap mat = null; |
381 | { | 480 | try |
382 | m_log.WarnFormat("User {0} can't edit object {1} {2}", agentID, sop.Name, sop.UUID); | 481 | { |
383 | continue; | 482 | mat = matsMap["Material"] as OSDMap; |
384 | } | 483 | } |
484 | catch (Exception e) | ||
485 | { | ||
486 | m_log.Warn("[Materials]: cannot decode \"Material\" from matsMap: " + e.Message); | ||
487 | continue; | ||
488 | } | ||
385 | 489 | ||
386 | Primitive.TextureEntry te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length); | 490 | Primitive.TextureEntry te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length); |
387 | if (te == null) | 491 | if (te == null) |
388 | { | 492 | { |
389 | m_log.WarnFormat("[Materials]: Error in TextureEntry for SOP {0} {1}", sop.Name, sop.UUID); | 493 | m_log.WarnFormat("[Materials]: Error in TextureEntry for SOP {0} {1}", sop.Name, sop.UUID); |
390 | continue; | 494 | continue; |
391 | } | 495 | } |
392 | 496 | ||
497 | UUID id; | ||
498 | if (mat == null) | ||
499 | { | ||
500 | // This happens then the user removes a material from a prim | ||
501 | id = UUID.Zero; | ||
502 | } | ||
503 | else | ||
504 | { | ||
505 | id = getNewID(mat); | ||
506 | } | ||
393 | 507 | ||
394 | UUID id; | 508 | int face = -1; |
395 | if (mat == null) | 509 | UUID oldid = UUID.Zero; |
396 | { | 510 | if (matsMap.ContainsKey("Face")) |
397 | // This happens then the user removes a material from a prim | 511 | { |
398 | id = UUID.Zero; | 512 | face = matsMap["Face"].AsInteger(); |
399 | } | 513 | Primitive.TextureEntryFace faceEntry = te.CreateFace((uint)face); |
514 | oldid = faceEntry.MaterialID; | ||
515 | faceEntry.MaterialID = id; | ||
516 | } | ||
517 | else | ||
518 | { | ||
519 | if (te.DefaultTexture == null) | ||
520 | m_log.WarnFormat("[Materials]: TextureEntry.DefaultTexture is null in {0} {1}", sop.Name, sop.UUID); | ||
400 | else | 521 | else |
401 | { | 522 | { |
402 | id = StoreMaterialAsAsset(agentID, mat, sop); | 523 | oldid = te.DefaultTexture.MaterialID; |
524 | te.DefaultTexture.MaterialID = id; | ||
403 | } | 525 | } |
526 | } | ||
404 | 527 | ||
528 | //m_log.DebugFormat("[Materials]: in \"{0}\" {1}, setting material ID for face {2} to {3}", sop.Name, sop.UUID, face, id); | ||
405 | 529 | ||
406 | int face = -1; | 530 | // We can't use sop.UpdateTextureEntry(te) because it filters, so do it manually |
531 | sop.Shape.TextureEntry = te.GetBytes(); | ||
407 | 532 | ||
408 | if (matsMap.ContainsKey("Face")) | 533 | lock(m_Materials) |
409 | { | 534 | { |
410 | face = matsMap["Face"].AsInteger(); | 535 | if(oldid != UUID.Zero && m_MaterialsRefCount.ContainsKey(oldid)) |
411 | Primitive.TextureEntryFace faceEntry = te.CreateFace((uint)face); | ||
412 | faceEntry.MaterialID = id; | ||
413 | } | ||
414 | else | ||
415 | { | 536 | { |
416 | if (te.DefaultTexture == null) | 537 | m_MaterialsRefCount[oldid]--; |
417 | m_log.WarnFormat("[Materials]: TextureEntry.DefaultTexture is null in {0} {1}", sop.Name, sop.UUID); | 538 | if(m_MaterialsRefCount[oldid] <= 0) |
418 | else | 539 | { |
419 | te.DefaultTexture.MaterialID = id; | 540 | m_Materials.Remove(oldid); |
541 | m_MaterialsRefCount.Remove(oldid); | ||
542 | m_cache.Expire(oldid.ToString()); | ||
543 | } | ||
420 | } | 544 | } |
421 | 545 | ||
422 | //m_log.DebugFormat("[Materials]: in \"{0}\" {1}, setting material ID for face {2} to {3}", sop.Name, sop.UUID, face, id); | 546 | if(id != UUID.Zero) |
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 | { | 547 | { |
429 | sop.TriggerScriptChangedEvent(Changed.TEXTURE); | 548 | AssetBase asset = CacheMaterialAsAsset(id, agentID, mat, sop); |
430 | sop.UpdateFlag = UpdateRequired.FULL; | 549 | if(asset != null) |
431 | sop.ParentGroup.HasGroupChanged = true; | 550 | { |
432 | sop.ScheduleFullUpdate(); | 551 | ulong materialHash = (ulong)primLocalID << 32; |
552 | if(face < 0) | ||
553 | materialHash += 0xffffffff; | ||
554 | else | ||
555 | materialHash +=(ulong)face; | ||
556 | |||
557 | m_changes[materialHash] = asset; | ||
558 | m_changesTime[materialHash] = Util.GetTimeStampMS(); | ||
559 | } | ||
433 | } | 560 | } |
434 | } | 561 | } |
562 | |||
563 | if(!parts.Contains(sop)) | ||
564 | parts.Add(sop); | ||
435 | } | 565 | } |
436 | catch (Exception e) | 566 | |
567 | foreach(SceneObjectPart sop in parts) | ||
437 | { | 568 | { |
438 | m_log.Warn("[Materials]: exception processing received material ", e); | 569 | if (sop.ParentGroup != null && !sop.ParentGroup.IsDeleted) |
570 | { | ||
571 | sop.TriggerScriptChangedEvent(Changed.TEXTURE); | ||
572 | sop.ScheduleFullUpdate(); | ||
573 | sop.ParentGroup.HasGroupChanged = true; | ||
574 | } | ||
439 | } | 575 | } |
440 | } | 576 | } |
577 | catch (Exception e) | ||
578 | { | ||
579 | m_log.Warn("[Materials]: exception processing received material ", e); | ||
580 | } | ||
441 | } | 581 | } |
442 | } | 582 | } |
443 | } | 583 | } |
444 | |||
445 | } | 584 | } |
446 | catch (Exception e) | 585 | catch (Exception e) |
447 | { | 586 | { |
@@ -450,7 +589,6 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
450 | } | 589 | } |
451 | } | 590 | } |
452 | 591 | ||
453 | |||
454 | resp["Zipped"] = ZCompressOSD(respArr, false); | 592 | resp["Zipped"] = ZCompressOSD(respArr, false); |
455 | string response = OSDParser.SerializeLLSDXmlString(resp); | 593 | string response = OSDParser.SerializeLLSDXmlString(resp); |
456 | 594 | ||
@@ -460,6 +598,40 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
460 | return response; | 598 | return response; |
461 | } | 599 | } |
462 | 600 | ||
601 | private UUID getNewID(OSDMap mat) | ||
602 | { | ||
603 | // ugly and done twice but keep compatibility for now | ||
604 | Byte[] data = System.Text.Encoding.ASCII.GetBytes(OSDParser.SerializeLLSDXmlString(mat)); | ||
605 | using (var md5 = MD5.Create()) | ||
606 | return new UUID(md5.ComputeHash(data), 0); | ||
607 | } | ||
608 | |||
609 | private AssetBase CacheMaterialAsAsset(UUID id, UUID agentID, OSDMap mat, SceneObjectPart sop) | ||
610 | { | ||
611 | AssetBase asset = null; | ||
612 | lock (m_Materials) | ||
613 | { | ||
614 | if (!m_Materials.ContainsKey(id)) | ||
615 | { | ||
616 | m_Materials[id] = mat; | ||
617 | m_MaterialsRefCount[id] = 1; | ||
618 | |||
619 | byte[] data = System.Text.Encoding.ASCII.GetBytes(OSDParser.SerializeLLSDXmlString(mat)); | ||
620 | |||
621 | // This asset might exist already, but it's ok to try to store it again | ||
622 | string name = "Material " + ChooseMaterialName(mat, sop); | ||
623 | name = name.Substring(0, Math.Min(64, name.Length)).Trim(); | ||
624 | asset = new AssetBase(id, name, (sbyte)OpenSimAssetType.Material, agentID.ToString()); | ||
625 | asset.Data = data; | ||
626 | asset.Local = true; | ||
627 | m_cache.Cache(asset); | ||
628 | } | ||
629 | else | ||
630 | m_MaterialsRefCount[id]++; | ||
631 | } | ||
632 | return asset; | ||
633 | } | ||
634 | |||
463 | private UUID StoreMaterialAsAsset(UUID agentID, OSDMap mat, SceneObjectPart sop) | 635 | private UUID StoreMaterialAsAsset(UUID agentID, OSDMap mat, SceneObjectPart sop) |
464 | { | 636 | { |
465 | UUID id; | 637 | UUID id; |
@@ -469,11 +641,12 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
469 | using (var md5 = MD5.Create()) | 641 | using (var md5 = MD5.Create()) |
470 | id = new UUID(md5.ComputeHash(data), 0); | 642 | id = new UUID(md5.ComputeHash(data), 0); |
471 | 643 | ||
472 | lock (m_regionMaterials) | 644 | lock (m_Materials) |
473 | { | 645 | { |
474 | if (!m_regionMaterials.ContainsKey(id)) | 646 | if (!m_Materials.ContainsKey(id)) |
475 | { | 647 | { |
476 | m_regionMaterials[id] = mat; | 648 | m_Materials[id] = mat; |
649 | m_MaterialsRefCount[id] = 1; | ||
477 | 650 | ||
478 | // This asset might exist already, but it's ok to try to store it again | 651 | // This asset might exist already, but it's ok to try to store it again |
479 | string name = "Material " + ChooseMaterialName(mat, sop); | 652 | string name = "Material " + ChooseMaterialName(mat, sop); |
@@ -482,6 +655,8 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
482 | asset.Data = data; | 655 | asset.Data = data; |
483 | m_scene.AssetService.Store(asset); | 656 | m_scene.AssetService.Store(asset); |
484 | } | 657 | } |
658 | else | ||
659 | m_MaterialsRefCount[id]++; | ||
485 | } | 660 | } |
486 | return id; | 661 | return id; |
487 | } | 662 | } |
@@ -509,7 +684,7 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
509 | 684 | ||
510 | if (sop.Name != "Primitive") | 685 | if (sop.Name != "Primitive") |
511 | return sop.Name; | 686 | return sop.Name; |
512 | 687 | ||
513 | if ((sop.ParentGroup != null) && (sop.ParentGroup.Name != "Primitive")) | 688 | if ((sop.ParentGroup != null) && (sop.ParentGroup.Name != "Primitive")) |
514 | return sop.ParentGroup.Name; | 689 | return sop.ParentGroup.Name; |
515 | 690 | ||
@@ -523,9 +698,9 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
523 | int matsCount = 0; | 698 | int matsCount = 0; |
524 | OSDArray allOsd = new OSDArray(); | 699 | OSDArray allOsd = new OSDArray(); |
525 | 700 | ||
526 | lock (m_regionMaterials) | 701 | lock (m_Materials) |
527 | { | 702 | { |
528 | foreach (KeyValuePair<UUID, OSDMap> kvp in m_regionMaterials) | 703 | foreach (KeyValuePair<UUID, OSDMap> kvp in m_Materials) |
529 | { | 704 | { |
530 | OSDMap matMap = new OSDMap(); | 705 | OSDMap matMap = new OSDMap(); |
531 | 706 | ||
@@ -573,7 +748,7 @@ namespace OpenSim.Region.OptionalModules.Materials | |||
573 | 748 | ||
574 | using (MemoryStream msSinkCompressed = new MemoryStream()) | 749 | using (MemoryStream msSinkCompressed = new MemoryStream()) |
575 | { | 750 | { |
576 | using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkCompressed, | 751 | using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkCompressed, |
577 | Ionic.Zlib.CompressionMode.Compress, CompressionLevel.BestCompression, true)) | 752 | Ionic.Zlib.CompressionMode.Compress, CompressionLevel.BestCompression, true)) |
578 | { | 753 | { |
579 | zOut.Write(data, 0, data.Length); | 754 | zOut.Write(data, 0, data.Length); |