From ca0336d8349382ddb46df4c7e7f6377c64151f25 Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Thu, 5 Dec 2013 14:18:59 +0200 Subject: Renamed MaterialsDemoModule to MaterialsModule --- .../OptionalModules/Materials/MaterialsModule.cs | 664 +++++++++++++++++++++ 1 file changed, 664 insertions(+) create mode 100644 OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs (limited to 'OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs') diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs new file mode 100644 index 0000000..e707154 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -0,0 +1,664 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Security.Cryptography; // for computing md5 hash +using log4net; +using Mono.Addins; +using Nini.Config; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +using Ionic.Zlib; + +// You will need to uncomment these lines if you are adding a region module to some other assembly which does not already +// specify its assembly. Otherwise, the region modules in the assembly will not be picked up when OpenSimulator scans +// the available DLLs +//[assembly: Addin("MaterialsDemoModule", "1.0")] +//[assembly: AddinDependency("OpenSim", "0.5")] + +namespace OpenSim.Region.OptionalModules.MaterialsDemoModule +{ + /// + /// + // # # ## ##### # # # # # #### + // # # # # # # ## # # ## # # # + // # # # # # # # # # # # # # # + // # ## # ###### ##### # # # # # # # # ### + // ## ## # # # # # ## # # ## # # + // # # # # # # # # # # # #### + // + // THIS MODULE IS FOR EXPERIMENTAL USE ONLY AND MAY CAUSE REGION OR ASSET CORRUPTION! + // + ////////////// WARNING ////////////////////////////////////////////////////////////////// + /// This is an *Experimental* module for developing support for materials-capable viewers + /// This module should NOT be used in a production environment! It may cause data corruption and + /// viewer crashes. It should be only used to evaluate implementations of materials. + /// + /// Materials are persisted via SceneObjectPart.dynattrs. This is a relatively new feature + /// of OpenSimulator and is not field proven at the time this module was written. Persistence + /// may fail or become corrupt and this could cause viewer crashes due to erroneous materials + /// data being sent to viewers. Materials descriptions might survive IAR, OAR, or other means + /// of archiving however the texture resources used by these materials probably will not as they + /// may not be adequately referenced to ensure proper archiving. + /// + /// + /// + /// To enable this module, add this string at the bottom of OpenSim.ini: + /// [MaterialsDemoModule] + /// + /// + /// + + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MaterialsDemoModule")] + public class MaterialsDemoModule : INonSharedRegionModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public string Name { get { return "MaterialsDemoModule"; } } + + public Type ReplaceableInterface { get { return null; } } + + private Scene m_scene = null; + private bool m_enabled = false; + + public Dictionary m_knownMaterials = new Dictionary(); + + public void Initialise(IConfigSource source) + { + m_enabled = (source.Configs["MaterialsDemoModule"] != null); + if (!m_enabled) + return; + + m_log.DebugFormat("[MaterialsDemoModule]: Initialized"); + } + + public void Close() + { + if (!m_enabled) + return; + + //m_log.DebugFormat("[MaterialsDemoModule]: CLOSED MODULE"); + } + + public void AddRegion(Scene scene) + { + if (!m_enabled) + return; + + //m_log.DebugFormat("[MaterialsDemoModule]: REGION {0} ADDED", scene.RegionInfo.RegionName); + + m_scene = scene; + m_scene.EventManager.OnRegisterCaps += OnRegisterCaps; + m_scene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene; +// m_scene.EventManager.OnGatherUuids += GatherMaterialsUuids; + } + + void EventManager_OnObjectAddedToScene(SceneObjectGroup obj) + { + foreach (var part in obj.Parts) + if (part != null) + GetStoredMaterialsForPart(part); + } + + void OnRegisterCaps(OpenMetaverse.UUID agentID, OpenSim.Framework.Capabilities.Caps caps) + { + string capsBase = "/CAPS/" + caps.CapsObjectPath; + + IRequestHandler renderMaterialsPostHandler + = new RestStreamHandler("POST", capsBase + "/", RenderMaterialsPostCap, "RenderMaterials", null); + caps.RegisterHandler("RenderMaterials", renderMaterialsPostHandler); + + // OpenSimulator CAPs infrastructure seems to be somewhat hostile towards any CAP that requires both GET + // and POST handlers, (at least at the time this was originally written), so we first set up a POST + // handler normally and then add a GET handler via MainServer + + IRequestHandler renderMaterialsGetHandler + = new RestStreamHandler("GET", capsBase + "/", RenderMaterialsGetCap, "RenderMaterials", null); + MainServer.Instance.AddStreamHandler(renderMaterialsGetHandler); + + // materials viewer seems to use either POST or PUT, so assign POST handler for PUT as well + IRequestHandler renderMaterialsPutHandler + = new RestStreamHandler("PUT", capsBase + "/", RenderMaterialsPostCap, "RenderMaterials", null); + MainServer.Instance.AddStreamHandler(renderMaterialsPutHandler); + } + + public void RemoveRegion(Scene scene) + { + if (!m_enabled) + return; + + m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps; + m_scene.EventManager.OnObjectAddedToScene -= EventManager_OnObjectAddedToScene; +// m_scene.EventManager.OnGatherUuids -= GatherMaterialsUuids; + + //m_log.DebugFormat("[MaterialsDemoModule]: REGION {0} REMOVED", scene.RegionInfo.RegionName); + } + + public void RegionLoaded(Scene scene) + { + } + + OSDMap GetMaterial(UUID id) + { + OSDMap map = null; + lock (m_knownMaterials) + { + if (m_knownMaterials.ContainsKey(id)) + { + map = new OSDMap(); + map["ID"] = OSD.FromBinary(id.GetBytes()); + map["Material"] = m_knownMaterials[id]; + } + } + return map; + } + + void GetStoredMaterialsForPart(SceneObjectPart part) + { + OSD OSMaterials = null; + OSDArray matsArr = null; + + if (part.DynAttrs == null) + { + //m_log.Warn("[MaterialsDemoModule]: NULL DYNATTRS :( "); + return; + } + + lock (part.DynAttrs) + { + if (part.DynAttrs.ContainsStore("OpenSim", "Materials")) + { + OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials"); + + if (materialsStore == null) + return; + + materialsStore.TryGetValue("Materials", out OSMaterials); + } + + if (OSMaterials != null && OSMaterials is OSDArray) + matsArr = OSMaterials as OSDArray; + else + return; + } + + //m_log.Info("[MaterialsDemoModule]: OSMaterials: " + OSDParser.SerializeJsonString(OSMaterials)); + + if (matsArr == null) + { + //m_log.Info("[MaterialsDemoModule]: matsArr is null :( "); + return; + } + + foreach (OSD elemOsd in matsArr) + { + if (elemOsd != null && elemOsd is OSDMap) + { + OSDMap matMap = elemOsd as OSDMap; + if (matMap.ContainsKey("ID") && matMap.ContainsKey("Material")) + { + try + { + lock (m_knownMaterials) + m_knownMaterials[matMap["ID"].AsUUID()] = (OSDMap)matMap["Material"]; + } + catch (Exception e) + { + m_log.Warn("[MaterialsDemoModule]: exception decoding persisted material ", e); + } + } + } + } + } + + void StoreMaterialsForPart(SceneObjectPart part) + { + try + { + if (part == null || part.Shape == null) + return; + + Dictionary mats = new Dictionary(); + + Primitive.TextureEntry te = part.Shape.Textures; + + if (te.DefaultTexture != null) + { + lock (m_knownMaterials) + { + if (m_knownMaterials.ContainsKey(te.DefaultTexture.MaterialID)) + mats[te.DefaultTexture.MaterialID] = m_knownMaterials[te.DefaultTexture.MaterialID]; + } + } + + if (te.FaceTextures != null) + { + foreach (var face in te.FaceTextures) + { + if (face != null) + { + lock (m_knownMaterials) + { + if (m_knownMaterials.ContainsKey(face.MaterialID)) + mats[face.MaterialID] = m_knownMaterials[face.MaterialID]; + } + } + } + } + if (mats.Count == 0) + return; + + OSDArray matsArr = new OSDArray(); + foreach (KeyValuePair kvp in mats) + { + OSDMap matOsd = new OSDMap(); + matOsd["ID"] = OSD.FromUUID(kvp.Key); + matOsd["Material"] = kvp.Value; + matsArr.Add(matOsd); + } + + OSDMap OSMaterials = new OSDMap(); + OSMaterials["Materials"] = matsArr; + + lock (part.DynAttrs) + part.DynAttrs.SetStore("OpenSim", "Materials", OSMaterials); + } + catch (Exception e) + { + m_log.Warn("[MaterialsDemoModule]: exception in StoreMaterialsForPart() ", e); + } + } + + public string RenderMaterialsPostCap(string request, string path, + string param, IOSHttpRequest httpRequest, + IOSHttpResponse httpResponse) + { + //m_log.Debug("[MaterialsDemoModule]: POST cap handler"); + + OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); + OSDMap resp = new OSDMap(); + + OSDMap materialsFromViewer = null; + + OSDArray respArr = new OSDArray(); + + if (req.ContainsKey("Zipped")) + { + OSD osd = null; + + byte[] inBytes = req["Zipped"].AsBinary(); + + try + { + osd = ZDecompressBytesToOsd(inBytes); + + if (osd != null) + { + if (osd is OSDArray) // assume array of MaterialIDs designating requested material entries + { + foreach (OSD elem in (OSDArray)osd) + { + + try + { + UUID id = new UUID(elem.AsBinary(), 0); + + lock (m_knownMaterials) + { + if (m_knownMaterials.ContainsKey(id)) + { + //m_log.Info("[MaterialsDemoModule]: request for known material ID: " + id.ToString()); + OSDMap matMap = new OSDMap(); + matMap["ID"] = OSD.FromBinary(id.GetBytes()); + + matMap["Material"] = m_knownMaterials[id]; + respArr.Add(matMap); + } + else + m_log.Info("[MaterialsDemoModule]: request for UNKNOWN material ID: " + id.ToString()); + } + } + catch (Exception e) + { + // report something here? + continue; + } + } + } + else if (osd is OSDMap) // reqest to assign a material + { + materialsFromViewer = osd as OSDMap; + + if (materialsFromViewer.ContainsKey("FullMaterialsPerFace")) + { + OSD matsOsd = materialsFromViewer["FullMaterialsPerFace"]; + if (matsOsd is OSDArray) + { + OSDArray matsArr = matsOsd as OSDArray; + + try + { + foreach (OSDMap matsMap in matsArr) + { + //m_log.Debug("[MaterialsDemoModule]: processing matsMap: " + OSDParser.SerializeJsonString(matsMap)); + + uint primLocalID = 0; + try { primLocalID = matsMap["ID"].AsUInteger(); } + catch (Exception e) { m_log.Warn("[MaterialsDemoModule]: cannot decode \"ID\" from matsMap: " + e.Message); } + //m_log.Debug("[MaterialsDemoModule]: primLocalID: " + primLocalID.ToString()); + + OSDMap mat = null; + try { mat = matsMap["Material"] as OSDMap; } + catch (Exception e) { m_log.Warn("[MaterialsDemoModule]: cannot decode \"Material\" from matsMap: " + e.Message); } + //m_log.Debug("[MaterialsDemoModule]: mat: " + OSDParser.SerializeJsonString(mat)); + + UUID id; + if (mat == null) + { + id = UUID.Zero; + } + else + { + id = HashOsd(mat); + lock (m_knownMaterials) + m_knownMaterials[id] = mat; + } + + var sop = m_scene.GetSceneObjectPart(primLocalID); + if (sop == null) + m_log.Debug("[MaterialsDemoModule]: null SOP for localId: " + primLocalID.ToString()); + else + { + var te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length); + + if (te == null) + { + m_log.Debug("[MaterialsDemoModule]: null TextureEntry for localId: " + primLocalID.ToString()); + } + else + { + int face = -1; + + if (matsMap.ContainsKey("Face")) + { + face = matsMap["Face"].AsInteger(); + if (te.FaceTextures == null) // && face == 0) + { + if (te.DefaultTexture == null) + m_log.Debug("[MaterialsDemoModule]: te.DefaultTexture is null"); + else + te.DefaultTexture.MaterialID = id; + } + else + { + if (te.FaceTextures.Length >= face - 1) + { + if (te.FaceTextures[face] == null) + te.DefaultTexture.MaterialID = id; + else + te.FaceTextures[face].MaterialID = id; + } + } + } + else + { + if (te.DefaultTexture != null) + te.DefaultTexture.MaterialID = id; + } + + //m_log.DebugFormat("[MaterialsDemoModule]: in \"{0}\", setting material ID for face {1} to {2}", sop.Name, face, id); + + //we cant use sop.UpdateTextureEntry(te); because it filters so do it manually + + if (sop.ParentGroup != null) + { + sop.Shape.TextureEntry = te.GetBytes(); + sop.TriggerScriptChangedEvent(Changed.TEXTURE); + sop.UpdateFlag = UpdateRequired.FULL; + sop.ParentGroup.HasGroupChanged = true; + + sop.ScheduleFullUpdate(); + + StoreMaterialsForPart(sop); + } + } + } + } + } + catch (Exception e) + { + m_log.Warn("[MaterialsDemoModule]: exception processing received material ", e); + } + } + } + } + } + + } + catch (Exception e) + { + m_log.Warn("[MaterialsDemoModule]: exception decoding zipped CAP payload ", e); + //return ""; + } + //m_log.Debug("[MaterialsDemoModule]: knownMaterials.Count: " + m_knownMaterials.Count.ToString()); + } + + + resp["Zipped"] = ZCompressOSD(respArr, false); + string response = OSDParser.SerializeLLSDXmlString(resp); + + //m_log.Debug("[MaterialsDemoModule]: cap request: " + request); + //m_log.Debug("[MaterialsDemoModule]: cap request (zipped portion): " + ZippedOsdBytesToString(req["Zipped"].AsBinary())); + //m_log.Debug("[MaterialsDemoModule]: cap response: " + response); + return response; + } + + + public string RenderMaterialsGetCap(string request, string path, + string param, IOSHttpRequest httpRequest, + IOSHttpResponse httpResponse) + { + //m_log.Debug("[MaterialsDemoModule]: GET cap handler"); + + OSDMap resp = new OSDMap(); + int matsCount = 0; + OSDArray allOsd = new OSDArray(); + + lock (m_knownMaterials) + { + foreach (KeyValuePair kvp in m_knownMaterials) + { + OSDMap matMap = new OSDMap(); + + matMap["ID"] = OSD.FromBinary(kvp.Key.GetBytes()); + matMap["Material"] = kvp.Value; + allOsd.Add(matMap); + matsCount++; + } + } + + resp["Zipped"] = ZCompressOSD(allOsd, false); + //m_log.Debug("[MaterialsDemoModule]: matsCount: " + matsCount.ToString()); + + return OSDParser.SerializeLLSDXmlString(resp); + } + + static string ZippedOsdBytesToString(byte[] bytes) + { + try + { + return OSDParser.SerializeJsonString(ZDecompressBytesToOsd(bytes)); + } + catch (Exception e) + { + return "ZippedOsdBytesToString caught an exception: " + e.ToString(); + } + } + + /// + /// computes a UUID by hashing a OSD object + /// + /// + /// + private static UUID HashOsd(OSD osd) + { + using (var md5 = MD5.Create()) + using (MemoryStream ms = new MemoryStream(OSDParser.SerializeLLSDBinary(osd, false))) + return new UUID(md5.ComputeHash(ms), 0); + } + + public static OSD ZCompressOSD(OSD inOsd, bool useHeader) + { + OSD osd = null; + + using (MemoryStream msSinkCompressed = new MemoryStream()) + { + using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkCompressed, + Ionic.Zlib.CompressionMode.Compress, CompressionLevel.BestCompression, true)) + { + CopyStream(new MemoryStream(OSDParser.SerializeLLSDBinary(inOsd, useHeader)), zOut); + zOut.Close(); + } + + msSinkCompressed.Seek(0L, SeekOrigin.Begin); + osd = OSD.FromBinary( msSinkCompressed.ToArray()); + } + + return osd; + } + + + public static OSD ZDecompressBytesToOsd(byte[] input) + { + OSD osd = null; + + using (MemoryStream msSinkUnCompressed = new MemoryStream()) + { + using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkUnCompressed, CompressionMode.Decompress, true)) + { + CopyStream(new MemoryStream(input), zOut); + zOut.Close(); + } + msSinkUnCompressed.Seek(0L, SeekOrigin.Begin); + osd = OSDParser.DeserializeLLSDBinary(msSinkUnCompressed.ToArray()); + } + + return osd; + } + + static void CopyStream(System.IO.Stream input, System.IO.Stream output) + { + byte[] buffer = new byte[2048]; + int len; + while ((len = input.Read(buffer, 0, 2048)) > 0) + { + output.Write(buffer, 0, len); + } + + output.Flush(); + } + + // FIXME: This code is currently still in UuidGatherer since we cannot use Scene.EventManager as some + // calls to the gatherer are done for objects with no scene. +// /// +// /// Gather all of the texture asset UUIDs used to reference "Materials" such as normal and specular maps +// /// +// /// +// /// +// private void GatherMaterialsUuids(SceneObjectPart part, IDictionary assetUuids) +// { +// // scan thru the dynAttrs map of this part for any textures used as materials +// OSD osdMaterials = null; +// +// lock (part.DynAttrs) +// { +// if (part.DynAttrs.ContainsStore("OpenSim", "Materials")) +// { +// OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials"); +// if (materialsStore == null) +// return; +// +// materialsStore.TryGetValue("Materials", out osdMaterials); +// } +// +// if (osdMaterials != null) +// { +// //m_log.Info("[UUID Gatherer]: found Materials: " + OSDParser.SerializeJsonString(osd)); +// +// if (osdMaterials is OSDArray) +// { +// OSDArray matsArr = osdMaterials as OSDArray; +// foreach (OSDMap matMap in matsArr) +// { +// try +// { +// if (matMap.ContainsKey("Material")) +// { +// OSDMap mat = matMap["Material"] as OSDMap; +// if (mat.ContainsKey("NormMap")) +// { +// UUID normalMapId = mat["NormMap"].AsUUID(); +// if (normalMapId != UUID.Zero) +// { +// assetUuids[normalMapId] = AssetType.Texture; +// //m_log.Info("[UUID Gatherer]: found normal map ID: " + normalMapId.ToString()); +// } +// } +// if (mat.ContainsKey("SpecMap")) +// { +// UUID specularMapId = mat["SpecMap"].AsUUID(); +// if (specularMapId != UUID.Zero) +// { +// assetUuids[specularMapId] = AssetType.Texture; +// //m_log.Info("[UUID Gatherer]: found specular map ID: " + specularMapId.ToString()); +// } +// } +// } +// +// } +// catch (Exception e) +// { +// m_log.Warn("[MaterialsDemoModule]: exception getting materials: " + e.Message); +// } +// } +// } +// } +// } +// } + } +} -- cgit v1.1 From 3018b2c5d7c9de0e8da6d158f0848c840b7864ab Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Fri, 6 Dec 2013 16:21:11 +0200 Subject: Materials module: a) Store materials as assets; b) Finalized it (removed the "Demo" label; removed most of the logging); c) Enabled by default Changed UuidGatherer to use 'sbyte' to identify assets instead of 'AssetType'. This lets UuidGatherer handle Materials, which are defined in a different enum from 'AssetType'. --- .../OptionalModules/Materials/MaterialsModule.cs | 518 ++++++++------------- 1 file changed, 191 insertions(+), 327 deletions(-) (limited to 'OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs') diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index e707154..09041e8 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -42,77 +42,49 @@ using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; +using OpenSimAssetType = OpenSim.Framework.SLUtil.OpenSimAssetType; using Ionic.Zlib; // You will need to uncomment these lines if you are adding a region module to some other assembly which does not already // specify its assembly. Otherwise, the region modules in the assembly will not be picked up when OpenSimulator scans // the available DLLs -//[assembly: Addin("MaterialsDemoModule", "1.0")] +//[assembly: Addin("MaterialsModule", "1.0")] //[assembly: AddinDependency("OpenSim", "0.5")] -namespace OpenSim.Region.OptionalModules.MaterialsDemoModule +namespace OpenSim.Region.OptionalModules.Materials { - /// - /// - // # # ## ##### # # # # # #### - // # # # # # # ## # # ## # # # - // # # # # # # # # # # # # # # - // # ## # ###### ##### # # # # # # # # ### - // ## ## # # # # # ## # # ## # # - // # # # # # # # # # # # #### - // - // THIS MODULE IS FOR EXPERIMENTAL USE ONLY AND MAY CAUSE REGION OR ASSET CORRUPTION! - // - ////////////// WARNING ////////////////////////////////////////////////////////////////// - /// This is an *Experimental* module for developing support for materials-capable viewers - /// This module should NOT be used in a production environment! It may cause data corruption and - /// viewer crashes. It should be only used to evaluate implementations of materials. - /// - /// Materials are persisted via SceneObjectPart.dynattrs. This is a relatively new feature - /// of OpenSimulator and is not field proven at the time this module was written. Persistence - /// may fail or become corrupt and this could cause viewer crashes due to erroneous materials - /// data being sent to viewers. Materials descriptions might survive IAR, OAR, or other means - /// of archiving however the texture resources used by these materials probably will not as they - /// may not be adequately referenced to ensure proper archiving. - /// - /// - /// - /// To enable this module, add this string at the bottom of OpenSim.ini: - /// [MaterialsDemoModule] - /// - /// - /// - - [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MaterialsDemoModule")] - public class MaterialsDemoModule : INonSharedRegionModule + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MaterialsModule")] + public class MaterialsModule : INonSharedRegionModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string Name { get { return "MaterialsDemoModule"; } } + public string Name { get { return "MaterialsModule"; } } public Type ReplaceableInterface { get { return null; } } private Scene m_scene = null; private bool m_enabled = false; - public Dictionary m_knownMaterials = new Dictionary(); + public Dictionary m_regionMaterials = new Dictionary(); public void Initialise(IConfigSource source) { - m_enabled = (source.Configs["MaterialsDemoModule"] != null); + IConfig config = source.Configs["Materials"]; + if (config == null) + return; + + m_enabled = config.GetBoolean("enable_materials", true); if (!m_enabled) return; - m_log.DebugFormat("[MaterialsDemoModule]: Initialized"); + m_log.DebugFormat("[Materials]: Initialized"); } public void Close() { if (!m_enabled) return; - - //m_log.DebugFormat("[MaterialsDemoModule]: CLOSED MODULE"); } public void AddRegion(Scene scene) @@ -120,22 +92,19 @@ namespace OpenSim.Region.OptionalModules.MaterialsDemoModule if (!m_enabled) return; - //m_log.DebugFormat("[MaterialsDemoModule]: REGION {0} ADDED", scene.RegionInfo.RegionName); - m_scene = scene; m_scene.EventManager.OnRegisterCaps += OnRegisterCaps; m_scene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene; -// m_scene.EventManager.OnGatherUuids += GatherMaterialsUuids; } - void EventManager_OnObjectAddedToScene(SceneObjectGroup obj) + private void EventManager_OnObjectAddedToScene(SceneObjectGroup obj) { foreach (var part in obj.Parts) if (part != null) - GetStoredMaterialsForPart(part); + GetStoredMaterialsInPart(part); } - void OnRegisterCaps(OpenMetaverse.UUID agentID, OpenSim.Framework.Capabilities.Caps caps) + private void OnRegisterCaps(OpenMetaverse.UUID agentID, OpenSim.Framework.Capabilities.Caps caps) { string capsBase = "/CAPS/" + caps.CapsObjectPath; @@ -164,143 +133,65 @@ namespace OpenSim.Region.OptionalModules.MaterialsDemoModule m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps; m_scene.EventManager.OnObjectAddedToScene -= EventManager_OnObjectAddedToScene; -// m_scene.EventManager.OnGatherUuids -= GatherMaterialsUuids; - - //m_log.DebugFormat("[MaterialsDemoModule]: REGION {0} REMOVED", scene.RegionInfo.RegionName); } public void RegionLoaded(Scene scene) { } - OSDMap GetMaterial(UUID id) - { - OSDMap map = null; - lock (m_knownMaterials) - { - if (m_knownMaterials.ContainsKey(id)) - { - map = new OSDMap(); - map["ID"] = OSD.FromBinary(id.GetBytes()); - map["Material"] = m_knownMaterials[id]; - } - } - return map; - } - - void GetStoredMaterialsForPart(SceneObjectPart part) + /// + /// Find the materials used in the SOP, and add them to 'm_regionMaterials'. + /// + private void GetStoredMaterialsInPart(SceneObjectPart part) { - OSD OSMaterials = null; - OSDArray matsArr = null; - - if (part.DynAttrs == null) - { - //m_log.Warn("[MaterialsDemoModule]: NULL DYNATTRS :( "); + if (part.Shape == null) return; - } - - lock (part.DynAttrs) - { - if (part.DynAttrs.ContainsStore("OpenSim", "Materials")) - { - OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials"); - - if (materialsStore == null) - return; - - materialsStore.TryGetValue("Materials", out OSMaterials); - } - - if (OSMaterials != null && OSMaterials is OSDArray) - matsArr = OSMaterials as OSDArray; - else - return; - } - - //m_log.Info("[MaterialsDemoModule]: OSMaterials: " + OSDParser.SerializeJsonString(OSMaterials)); - - if (matsArr == null) - { - //m_log.Info("[MaterialsDemoModule]: matsArr is null :( "); + var te = new Primitive.TextureEntry(part.Shape.TextureEntry, 0, part.Shape.TextureEntry.Length); + if (te == null) return; - } - foreach (OSD elemOsd in matsArr) + GetStoredMaterialInFace(part, te.DefaultTexture); + + foreach (Primitive.TextureEntryFace face in te.FaceTextures) { - if (elemOsd != null && elemOsd is OSDMap) - { - OSDMap matMap = elemOsd as OSDMap; - if (matMap.ContainsKey("ID") && matMap.ContainsKey("Material")) - { - try - { - lock (m_knownMaterials) - m_knownMaterials[matMap["ID"].AsUUID()] = (OSDMap)matMap["Material"]; - } - catch (Exception e) - { - m_log.Warn("[MaterialsDemoModule]: exception decoding persisted material ", e); - } - } - } + if (face != null) + GetStoredMaterialInFace(part, face); } } - void StoreMaterialsForPart(SceneObjectPart part) + /// + /// Find the materials used in one Face, and add them to 'm_regionMaterials'. + /// + private void GetStoredMaterialInFace(SceneObjectPart part, Primitive.TextureEntryFace face) { - try + UUID id = face.MaterialID; + if (id == UUID.Zero) + return; + + lock (m_regionMaterials) { - if (part == null || part.Shape == null) + if (m_regionMaterials.ContainsKey(id)) return; - - Dictionary mats = new Dictionary(); - - Primitive.TextureEntry te = part.Shape.Textures; - - if (te.DefaultTexture != null) + + byte[] data = m_scene.AssetService.GetData(id.ToString()); + if (data == null) { - lock (m_knownMaterials) - { - if (m_knownMaterials.ContainsKey(te.DefaultTexture.MaterialID)) - mats[te.DefaultTexture.MaterialID] = m_knownMaterials[te.DefaultTexture.MaterialID]; - } + m_log.WarnFormat("[Materials]: Prim \"{0}\" ({1}) contains unknown material ID {2}", part.Name, part.UUID, id); + return; } - if (te.FaceTextures != null) + OSDMap mat; + try { - foreach (var face in te.FaceTextures) - { - if (face != null) - { - lock (m_knownMaterials) - { - if (m_knownMaterials.ContainsKey(face.MaterialID)) - mats[face.MaterialID] = m_knownMaterials[face.MaterialID]; - } - } - } + mat = (OSDMap)OSDParser.DeserializeLLSDXml(data); } - if (mats.Count == 0) - return; - - OSDArray matsArr = new OSDArray(); - foreach (KeyValuePair kvp in mats) + catch (Exception e) { - OSDMap matOsd = new OSDMap(); - matOsd["ID"] = OSD.FromUUID(kvp.Key); - matOsd["Material"] = kvp.Value; - matsArr.Add(matOsd); + m_log.WarnFormat("[Materials]: cannot decode material asset {0}: {1}", id, e.Message); + return; } - OSDMap OSMaterials = new OSDMap(); - OSMaterials["Materials"] = matsArr; - - lock (part.DynAttrs) - part.DynAttrs.SetStore("OpenSim", "Materials", OSMaterials); - } - catch (Exception e) - { - m_log.Warn("[MaterialsDemoModule]: exception in StoreMaterialsForPart() ", e); + m_regionMaterials[id] = mat; } } @@ -308,8 +199,6 @@ namespace OpenSim.Region.OptionalModules.MaterialsDemoModule string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { - //m_log.Debug("[MaterialsDemoModule]: POST cap handler"); - OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); OSDMap resp = new OSDMap(); @@ -333,34 +222,38 @@ namespace OpenSim.Region.OptionalModules.MaterialsDemoModule { foreach (OSD elem in (OSDArray)osd) { - try { UUID id = new UUID(elem.AsBinary(), 0); - lock (m_knownMaterials) + lock (m_regionMaterials) { - if (m_knownMaterials.ContainsKey(id)) + if (m_regionMaterials.ContainsKey(id)) { - //m_log.Info("[MaterialsDemoModule]: request for known material ID: " + id.ToString()); OSDMap matMap = new OSDMap(); matMap["ID"] = OSD.FromBinary(id.GetBytes()); - - matMap["Material"] = m_knownMaterials[id]; + matMap["Material"] = m_regionMaterials[id]; respArr.Add(matMap); } else - m_log.Info("[MaterialsDemoModule]: request for UNKNOWN material ID: " + id.ToString()); + { + m_log.Warn("[Materials]: request for unknown material ID: " + id.ToString()); + + // Theoretically we could try to load the material from the assets service, + // but that shouldn't be necessary because the viewer should only request + // materials that exist in a prim on the region, and all of these materials + // are already stored in m_regionMaterials. + } } } catch (Exception e) { - // report something here? + m_log.Error("Error getting materials in response to viewer request", e); continue; } } } - else if (osd is OSDMap) // reqest to assign a material + else if (osd is OSDMap) // request to assign a material { materialsFromViewer = osd as OSDMap; @@ -375,94 +268,118 @@ namespace OpenSim.Region.OptionalModules.MaterialsDemoModule { foreach (OSDMap matsMap in matsArr) { - //m_log.Debug("[MaterialsDemoModule]: processing matsMap: " + OSDParser.SerializeJsonString(matsMap)); - uint primLocalID = 0; - try { primLocalID = matsMap["ID"].AsUInteger(); } - catch (Exception e) { m_log.Warn("[MaterialsDemoModule]: cannot decode \"ID\" from matsMap: " + e.Message); } - //m_log.Debug("[MaterialsDemoModule]: primLocalID: " + primLocalID.ToString()); + try { + primLocalID = matsMap["ID"].AsUInteger(); + } + catch (Exception e) { + m_log.Warn("[Materials]: cannot decode \"ID\" from matsMap: " + e.Message); + continue; + } OSDMap mat = null; - try { mat = matsMap["Material"] as OSDMap; } - catch (Exception e) { m_log.Warn("[MaterialsDemoModule]: cannot decode \"Material\" from matsMap: " + e.Message); } - //m_log.Debug("[MaterialsDemoModule]: mat: " + OSDParser.SerializeJsonString(mat)); + try + { + mat = matsMap["Material"] as OSDMap; + } + catch (Exception e) + { + m_log.Warn("[Materials]: cannot decode \"Material\" from matsMap: " + e.Message); + continue; + } + + SceneObjectPart sop = m_scene.GetSceneObjectPart(primLocalID); + if (sop == null) + { + m_log.WarnFormat("[Materials]: SOP not found for localId: {0}", primLocalID.ToString()); + continue; + } + + Primitive.TextureEntry te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length); + if (te == null) + { + m_log.WarnFormat("[Materials]: Error in TextureEntry for SOP {0} {1}", sop.Name, sop.UUID); + continue; + } + UUID id; if (mat == null) { + // This happens then the user removes a material from a prim id = UUID.Zero; } else { - id = HashOsd(mat); - lock (m_knownMaterials) - m_knownMaterials[id] = mat; + // Material UUID = hash of the material's data. + // This makes materials deduplicate across the entire grid (but isn't otherwise required). + byte[] data = System.Text.Encoding.ASCII.GetBytes(OSDParser.SerializeLLSDXmlString(mat)); + using (var md5 = MD5.Create()) + id = new UUID(md5.ComputeHash(data), 0); + + lock (m_regionMaterials) + { + if (!m_regionMaterials.ContainsKey(id)) + { + m_regionMaterials[id] = mat; + + // This asset might exist already, but it's ok to try to store it again + string name = "Material " + ChooseMaterialName(mat, sop); + name = name.Substring(0, Math.Min(64, name.Length)).Trim(); + AssetBase asset = new AssetBase(id, name, (sbyte)OpenSimAssetType.Material, sop.OwnerID.ToString()); + asset.Data = data; + m_scene.AssetService.Store(asset); + } + } } - var sop = m_scene.GetSceneObjectPart(primLocalID); - if (sop == null) - m_log.Debug("[MaterialsDemoModule]: null SOP for localId: " + primLocalID.ToString()); - else - { - var te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length); - if (te == null) + int face = -1; + + if (matsMap.ContainsKey("Face")) + { + face = matsMap["Face"].AsInteger(); + if (te.FaceTextures == null) // && face == 0) { - m_log.Debug("[MaterialsDemoModule]: null TextureEntry for localId: " + primLocalID.ToString()); + if (te.DefaultTexture == null) + m_log.WarnFormat("[Materials]: te.DefaultTexture is null in {0} {1}", sop.Name, sop.UUID); + else + te.DefaultTexture.MaterialID = id; } else { - int face = -1; - - if (matsMap.ContainsKey("Face")) - { - face = matsMap["Face"].AsInteger(); - if (te.FaceTextures == null) // && face == 0) - { - if (te.DefaultTexture == null) - m_log.Debug("[MaterialsDemoModule]: te.DefaultTexture is null"); - else - te.DefaultTexture.MaterialID = id; - } - else - { - if (te.FaceTextures.Length >= face - 1) - { - if (te.FaceTextures[face] == null) - te.DefaultTexture.MaterialID = id; - else - te.FaceTextures[face].MaterialID = id; - } - } - } - else + if (te.FaceTextures.Length >= face - 1) { - if (te.DefaultTexture != null) + if (te.FaceTextures[face] == null) te.DefaultTexture.MaterialID = id; + else + te.FaceTextures[face].MaterialID = id; } + } + } + else + { + if (te.DefaultTexture != null) + te.DefaultTexture.MaterialID = id; + } - //m_log.DebugFormat("[MaterialsDemoModule]: in \"{0}\", setting material ID for face {1} to {2}", sop.Name, face, id); - - //we cant use sop.UpdateTextureEntry(te); because it filters so do it manually - - if (sop.ParentGroup != null) - { - sop.Shape.TextureEntry = te.GetBytes(); - sop.TriggerScriptChangedEvent(Changed.TEXTURE); - sop.UpdateFlag = UpdateRequired.FULL; - sop.ParentGroup.HasGroupChanged = true; + //m_log.DebugFormat("[Materials]: in \"{0}\" {1}, setting material ID for face {2} to {3}", sop.Name, sop.UUID, face, id); - sop.ScheduleFullUpdate(); + // We can't use sop.UpdateTextureEntry(te) because it filters, so do it manually + sop.Shape.TextureEntry = te.GetBytes(); - StoreMaterialsForPart(sop); - } - } + if (sop.ParentGroup != null) + { + sop.TriggerScriptChangedEvent(Changed.TEXTURE); + sop.UpdateFlag = UpdateRequired.FULL; + sop.ParentGroup.HasGroupChanged = true; + sop.ScheduleFullUpdate(); } } } catch (Exception e) { - m_log.Warn("[MaterialsDemoModule]: exception processing received material ", e); + m_log.Warn("[Materials]: exception processing received material ", e); } } } @@ -472,36 +389,63 @@ namespace OpenSim.Region.OptionalModules.MaterialsDemoModule } catch (Exception e) { - m_log.Warn("[MaterialsDemoModule]: exception decoding zipped CAP payload ", e); + m_log.Warn("[Materials]: exception decoding zipped CAP payload ", e); //return ""; } - //m_log.Debug("[MaterialsDemoModule]: knownMaterials.Count: " + m_knownMaterials.Count.ToString()); } resp["Zipped"] = ZCompressOSD(respArr, false); string response = OSDParser.SerializeLLSDXmlString(resp); - //m_log.Debug("[MaterialsDemoModule]: cap request: " + request); - //m_log.Debug("[MaterialsDemoModule]: cap request (zipped portion): " + ZippedOsdBytesToString(req["Zipped"].AsBinary())); - //m_log.Debug("[MaterialsDemoModule]: cap response: " + response); + //m_log.Debug("[Materials]: cap request: " + request); + //m_log.Debug("[Materials]: cap request (zipped portion): " + ZippedOsdBytesToString(req["Zipped"].AsBinary())); + //m_log.Debug("[Materials]: cap response: " + response); return response; } + /// + /// Use heuristics to choose a good name for the material. + /// + private string ChooseMaterialName(OSDMap mat, SceneObjectPart sop) + { + UUID normMap = mat["NormMap"].AsUUID(); + if (normMap != UUID.Zero) + { + AssetBase asset = m_scene.AssetService.GetCached(normMap.ToString()); + if ((asset != null) && (asset.Name.Length > 0) && !asset.Name.Equals("From IAR")) + return asset.Name; + } + + UUID specMap = mat["SpecMap"].AsUUID(); + if (specMap != UUID.Zero) + { + AssetBase asset = m_scene.AssetService.GetCached(specMap.ToString()); + if ((asset != null) && (asset.Name.Length > 0) && !asset.Name.Equals("From IAR")) + return asset.Name; + } + + if (sop.Name != "Primitive") + return sop.Name; + + if ((sop.ParentGroup != null) && (sop.ParentGroup.Name != "Primitive")) + return sop.ParentGroup.Name; + + return ""; + } + public string RenderMaterialsGetCap(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { - //m_log.Debug("[MaterialsDemoModule]: GET cap handler"); - OSDMap resp = new OSDMap(); int matsCount = 0; OSDArray allOsd = new OSDArray(); - lock (m_knownMaterials) + lock (m_regionMaterials) { - foreach (KeyValuePair kvp in m_knownMaterials) + foreach (KeyValuePair kvp in m_regionMaterials) { OSDMap matMap = new OSDMap(); @@ -513,12 +457,11 @@ namespace OpenSim.Region.OptionalModules.MaterialsDemoModule } resp["Zipped"] = ZCompressOSD(allOsd, false); - //m_log.Debug("[MaterialsDemoModule]: matsCount: " + matsCount.ToString()); return OSDParser.SerializeLLSDXmlString(resp); } - static string ZippedOsdBytesToString(byte[] bytes) + private static string ZippedOsdBytesToString(byte[] bytes) { try { @@ -537,26 +480,27 @@ namespace OpenSim.Region.OptionalModules.MaterialsDemoModule /// private static UUID HashOsd(OSD osd) { + byte[] data = OSDParser.SerializeLLSDBinary(osd, false); using (var md5 = MD5.Create()) - using (MemoryStream ms = new MemoryStream(OSDParser.SerializeLLSDBinary(osd, false))) - return new UUID(md5.ComputeHash(ms), 0); + return new UUID(md5.ComputeHash(data), 0); } public static OSD ZCompressOSD(OSD inOsd, bool useHeader) { OSD osd = null; + byte[] data = OSDParser.SerializeLLSDBinary(inOsd, useHeader); + using (MemoryStream msSinkCompressed = new MemoryStream()) { using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkCompressed, Ionic.Zlib.CompressionMode.Compress, CompressionLevel.BestCompression, true)) { - CopyStream(new MemoryStream(OSDParser.SerializeLLSDBinary(inOsd, useHeader)), zOut); - zOut.Close(); + zOut.Write(data, 0, data.Length); } msSinkCompressed.Seek(0L, SeekOrigin.Begin); - osd = OSD.FromBinary( msSinkCompressed.ToArray()); + osd = OSD.FromBinary(msSinkCompressed.ToArray()); } return osd; @@ -571,94 +515,14 @@ namespace OpenSim.Region.OptionalModules.MaterialsDemoModule { using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkUnCompressed, CompressionMode.Decompress, true)) { - CopyStream(new MemoryStream(input), zOut); - zOut.Close(); + zOut.Write(input, 0, input.Length); } + msSinkUnCompressed.Seek(0L, SeekOrigin.Begin); osd = OSDParser.DeserializeLLSDBinary(msSinkUnCompressed.ToArray()); } return osd; } - - static void CopyStream(System.IO.Stream input, System.IO.Stream output) - { - byte[] buffer = new byte[2048]; - int len; - while ((len = input.Read(buffer, 0, 2048)) > 0) - { - output.Write(buffer, 0, len); - } - - output.Flush(); - } - - // FIXME: This code is currently still in UuidGatherer since we cannot use Scene.EventManager as some - // calls to the gatherer are done for objects with no scene. -// /// -// /// Gather all of the texture asset UUIDs used to reference "Materials" such as normal and specular maps -// /// -// /// -// /// -// private void GatherMaterialsUuids(SceneObjectPart part, IDictionary assetUuids) -// { -// // scan thru the dynAttrs map of this part for any textures used as materials -// OSD osdMaterials = null; -// -// lock (part.DynAttrs) -// { -// if (part.DynAttrs.ContainsStore("OpenSim", "Materials")) -// { -// OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials"); -// if (materialsStore == null) -// return; -// -// materialsStore.TryGetValue("Materials", out osdMaterials); -// } -// -// if (osdMaterials != null) -// { -// //m_log.Info("[UUID Gatherer]: found Materials: " + OSDParser.SerializeJsonString(osd)); -// -// if (osdMaterials is OSDArray) -// { -// OSDArray matsArr = osdMaterials as OSDArray; -// foreach (OSDMap matMap in matsArr) -// { -// try -// { -// if (matMap.ContainsKey("Material")) -// { -// OSDMap mat = matMap["Material"] as OSDMap; -// if (mat.ContainsKey("NormMap")) -// { -// UUID normalMapId = mat["NormMap"].AsUUID(); -// if (normalMapId != UUID.Zero) -// { -// assetUuids[normalMapId] = AssetType.Texture; -// //m_log.Info("[UUID Gatherer]: found normal map ID: " + normalMapId.ToString()); -// } -// } -// if (mat.ContainsKey("SpecMap")) -// { -// UUID specularMapId = mat["SpecMap"].AsUUID(); -// if (specularMapId != UUID.Zero) -// { -// assetUuids[specularMapId] = AssetType.Texture; -// //m_log.Info("[UUID Gatherer]: found specular map ID: " + specularMapId.ToString()); -// } -// } -// } -// -// } -// catch (Exception e) -// { -// m_log.Warn("[MaterialsDemoModule]: exception getting materials: " + e.Message); -// } -// } -// } -// } -// } -// } } } -- cgit v1.1 From 68d83425c6b39614210b28e97d5006a882ea3097 Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Thu, 12 Dec 2013 15:14:24 +0200 Subject: When asked to change the Material for one face, change only that face; not the default material --- .../OptionalModules/Materials/MaterialsModule.cs | 23 +++++----------------- 1 file changed, 5 insertions(+), 18 deletions(-) (limited to 'OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs') diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index 09041e8..9779594 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -339,27 +339,14 @@ namespace OpenSim.Region.OptionalModules.Materials if (matsMap.ContainsKey("Face")) { face = matsMap["Face"].AsInteger(); - if (te.FaceTextures == null) // && face == 0) - { - if (te.DefaultTexture == null) - m_log.WarnFormat("[Materials]: te.DefaultTexture is null in {0} {1}", sop.Name, sop.UUID); - else - te.DefaultTexture.MaterialID = id; - } - else - { - if (te.FaceTextures.Length >= face - 1) - { - if (te.FaceTextures[face] == null) - te.DefaultTexture.MaterialID = id; - else - te.FaceTextures[face].MaterialID = id; - } - } + Primitive.TextureEntryFace faceEntry = te.CreateFace((uint)face); + faceEntry.MaterialID = id; } else { - if (te.DefaultTexture != null) + if (te.DefaultTexture == null) + m_log.WarnFormat("[Materials]: TextureEntry.DefaultTexture is null in {0} {1}", sop.Name, sop.UUID); + else te.DefaultTexture.MaterialID = id; } -- cgit v1.1 From d1f16c4b4b3f5c0938f3f0572c70e92cb90b6a0b Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Sun, 5 Jan 2014 14:03:10 +0200 Subject: Check agent permissions before modifying an object's materials. Also, when creating a Material asset, set the current agent as the Creator. --- .../OptionalModules/Materials/MaterialsModule.cs | 31 +++++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs') diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index 9779594..4b635d8 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -109,7 +109,10 @@ namespace OpenSim.Region.OptionalModules.Materials string capsBase = "/CAPS/" + caps.CapsObjectPath; IRequestHandler renderMaterialsPostHandler - = new RestStreamHandler("POST", capsBase + "/", RenderMaterialsPostCap, "RenderMaterials", null); + = new RestStreamHandler("POST", capsBase + "/", + (request, path, param, httpRequest, httpResponse) + => RenderMaterialsPostCap(request, agentID), + "RenderMaterials", null); caps.RegisterHandler("RenderMaterials", renderMaterialsPostHandler); // OpenSimulator CAPs infrastructure seems to be somewhat hostile towards any CAP that requires both GET @@ -117,12 +120,18 @@ namespace OpenSim.Region.OptionalModules.Materials // handler normally and then add a GET handler via MainServer IRequestHandler renderMaterialsGetHandler - = new RestStreamHandler("GET", capsBase + "/", RenderMaterialsGetCap, "RenderMaterials", null); + = new RestStreamHandler("GET", capsBase + "/", + (request, path, param, httpRequest, httpResponse) + => RenderMaterialsGetCap(request), + "RenderMaterials", null); MainServer.Instance.AddStreamHandler(renderMaterialsGetHandler); // materials viewer seems to use either POST or PUT, so assign POST handler for PUT as well IRequestHandler renderMaterialsPutHandler - = new RestStreamHandler("PUT", capsBase + "/", RenderMaterialsPostCap, "RenderMaterials", null); + = new RestStreamHandler("PUT", capsBase + "/", + (request, path, param, httpRequest, httpResponse) + => RenderMaterialsPostCap(request, agentID), + "RenderMaterials", null); MainServer.Instance.AddStreamHandler(renderMaterialsPutHandler); } @@ -195,9 +204,7 @@ namespace OpenSim.Region.OptionalModules.Materials } } - public string RenderMaterialsPostCap(string request, string path, - string param, IOSHttpRequest httpRequest, - IOSHttpResponse httpResponse) + public string RenderMaterialsPostCap(string request, UUID agentID) { OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); OSDMap resp = new OSDMap(); @@ -295,6 +302,12 @@ namespace OpenSim.Region.OptionalModules.Materials continue; } + if (!m_scene.Permissions.CanEditObject(sop.UUID, agentID)) + { + m_log.WarnFormat("User {0} can't edit object {1} {2}", agentID, sop.Name, sop.UUID); + continue; + } + Primitive.TextureEntry te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length); if (te == null) { @@ -326,7 +339,7 @@ namespace OpenSim.Region.OptionalModules.Materials // This asset might exist already, but it's ok to try to store it again string name = "Material " + ChooseMaterialName(mat, sop); name = name.Substring(0, Math.Min(64, name.Length)).Trim(); - AssetBase asset = new AssetBase(id, name, (sbyte)OpenSimAssetType.Material, sop.OwnerID.ToString()); + AssetBase asset = new AssetBase(id, name, (sbyte)OpenSimAssetType.Material, agentID.ToString()); asset.Data = data; m_scene.AssetService.Store(asset); } @@ -422,9 +435,7 @@ namespace OpenSim.Region.OptionalModules.Materials } - public string RenderMaterialsGetCap(string request, string path, - string param, IOSHttpRequest httpRequest, - IOSHttpResponse httpResponse) + public string RenderMaterialsGetCap(string request) { OSDMap resp = new OSDMap(); int matsCount = 0; -- cgit v1.1 From 28723beb0ccec654ac24ee1632b137b424fd3360 Mon Sep 17 00:00:00 2001 From: dahlia Date: Mon, 20 Jan 2014 02:57:08 -0800 Subject: Add code to convert legacy materials stored in DynAttrs to new asset format and store them as assets --- .../OptionalModules/Materials/MaterialsModule.cs | 122 +++++++++++++++++---- 1 file changed, 102 insertions(+), 20 deletions(-) (limited to 'OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs') diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index 4b635d8..1b5a7a3 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -149,12 +149,87 @@ namespace OpenSim.Region.OptionalModules.Materials } /// + /// Searches the part for any legacy materials stored in DynAttrs and converts them to assets, replacing + /// the MaterialIDs in the TextureEntries for the part. + /// Deletes the legacy materials from the part as they are no longer needed. + /// + /// + private void ConvertLegacyMaterialsInPart(SceneObjectPart part) + { + if (part.DynAttrs == null) + return; + + var te = new Primitive.TextureEntry(part.Shape.TextureEntry, 0, part.Shape.TextureEntry.Length); + if (te == null) + return; + + OSD OSMaterials = null; + OSDArray matsArr = null; + + lock (part.DynAttrs) + { + if (part.DynAttrs.ContainsStore("OpenSim", "Materials")) + { + OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials"); + + if (materialsStore == null) + return; + + materialsStore.TryGetValue("Materials", out OSMaterials); + } + + if (OSMaterials != null && OSMaterials is OSDArray) + matsArr = OSMaterials as OSDArray; + else + return; + } + + if (matsArr == null) + return; + + foreach (OSD elemOsd in matsArr) + { + if (elemOsd != null && elemOsd is OSDMap) + { + OSDMap matMap = elemOsd as OSDMap; + if (matMap.ContainsKey("ID") && matMap.ContainsKey("Material")) + { + UUID id = matMap["ID"].AsUUID(); + OSDMap material = (OSDMap)matMap["Material"]; + bool used = false; + + foreach (var face in te.FaceTextures) + if (face.MaterialID == id) + used = true; + + if (used) + { // store legacy material in new asset format, and update the part texture entry with the new hashed UUID + + var newId = StoreMaterialAsAsset(part.CreatorID, material, part); + foreach (var face in te.FaceTextures) + if (face.MaterialID == id) + face.MaterialID = newId; + } + } + } + } + + part.Shape.TextureEntry = te.GetBytes(); + + lock (part.DynAttrs) + part.DynAttrs.RemoveStore("OpenSim", "Materials"); + } + + /// /// Find the materials used in the SOP, and add them to 'm_regionMaterials'. /// private void GetStoredMaterialsInPart(SceneObjectPart part) { if (part.Shape == null) return; + + ConvertLegacyMaterialsInPart(part); + var te = new Primitive.TextureEntry(part.Shape.TextureEntry, 0, part.Shape.TextureEntry.Length); if (te == null) return; @@ -324,26 +399,7 @@ namespace OpenSim.Region.OptionalModules.Materials } else { - // Material UUID = hash of the material's data. - // This makes materials deduplicate across the entire grid (but isn't otherwise required). - byte[] data = System.Text.Encoding.ASCII.GetBytes(OSDParser.SerializeLLSDXmlString(mat)); - using (var md5 = MD5.Create()) - id = new UUID(md5.ComputeHash(data), 0); - - lock (m_regionMaterials) - { - if (!m_regionMaterials.ContainsKey(id)) - { - m_regionMaterials[id] = mat; - - // This asset might exist already, but it's ok to try to store it again - string name = "Material " + ChooseMaterialName(mat, sop); - name = name.Substring(0, Math.Min(64, name.Length)).Trim(); - AssetBase asset = new AssetBase(id, name, (sbyte)OpenSimAssetType.Material, agentID.ToString()); - asset.Data = data; - m_scene.AssetService.Store(asset); - } - } + id = StoreMaterialAsAsset(agentID, mat, sop); } @@ -404,6 +460,32 @@ namespace OpenSim.Region.OptionalModules.Materials return response; } + private UUID StoreMaterialAsAsset(UUID agentID, OSDMap mat, SceneObjectPart sop) + { + UUID id; + // Material UUID = hash of the material's data. + // This makes materials deduplicate across the entire grid (but isn't otherwise required). + byte[] data = System.Text.Encoding.ASCII.GetBytes(OSDParser.SerializeLLSDXmlString(mat)); + using (var md5 = MD5.Create()) + id = new UUID(md5.ComputeHash(data), 0); + + lock (m_regionMaterials) + { + if (!m_regionMaterials.ContainsKey(id)) + { + m_regionMaterials[id] = mat; + + // This asset might exist already, but it's ok to try to store it again + string name = "Material " + ChooseMaterialName(mat, sop); + name = name.Substring(0, Math.Min(64, name.Length)).Trim(); + AssetBase asset = new AssetBase(id, name, (sbyte)OpenSimAssetType.Material, agentID.ToString()); + asset.Data = data; + m_scene.AssetService.Store(asset); + } + } + return id; + } + /// /// Use heuristics to choose a good name for the material. /// -- cgit v1.1 From 95c926b2cd8585dd5b84ad7827d21b6122ea1001 Mon Sep 17 00:00:00 2001 From: dahlia Date: Mon, 20 Jan 2014 03:02:30 -0800 Subject: delay texture entry parsing until absolutely necessary while converting legacy materials --- OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs') diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index 1b5a7a3..d8ec979 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -159,10 +159,6 @@ namespace OpenSim.Region.OptionalModules.Materials if (part.DynAttrs == null) return; - var te = new Primitive.TextureEntry(part.Shape.TextureEntry, 0, part.Shape.TextureEntry.Length); - if (te == null) - return; - OSD OSMaterials = null; OSDArray matsArr = null; @@ -187,6 +183,10 @@ namespace OpenSim.Region.OptionalModules.Materials if (matsArr == null) return; + var te = new Primitive.TextureEntry(part.Shape.TextureEntry, 0, part.Shape.TextureEntry.Length); + if (te == null) + return; + foreach (OSD elemOsd in matsArr) { if (elemOsd != null && elemOsd is OSDMap) -- cgit v1.1 From 36d8a24a867fbbc95214653fec463aced8ba7c5f Mon Sep 17 00:00:00 2001 From: dahlia Date: Mon, 20 Jan 2014 03:11:01 -0800 Subject: force SOG update when converting legacy materials to ensure changes are persisted --- OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs') diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index d8ec979..ce2a56a 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -215,6 +215,8 @@ namespace OpenSim.Region.OptionalModules.Materials } part.Shape.TextureEntry = te.GetBytes(); + part.ParentGroup.HasGroupChanged = true; + part.ScheduleFullUpdate(); lock (part.DynAttrs) part.DynAttrs.RemoveStore("OpenSim", "Materials"); -- cgit v1.1 From 1cae3664a52fe48965954afc19804b11720c4add Mon Sep 17 00:00:00 2001 From: dahlia Date: Mon, 20 Jan 2014 11:53:33 -0800 Subject: add null texture entry face check before converting legacy materials --- OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs') diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index ce2a56a..c4bc8a0 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -199,7 +199,7 @@ namespace OpenSim.Region.OptionalModules.Materials bool used = false; foreach (var face in te.FaceTextures) - if (face.MaterialID == id) + if (face != null && face.MaterialID == id) used = true; if (used) @@ -207,7 +207,7 @@ namespace OpenSim.Region.OptionalModules.Materials var newId = StoreMaterialAsAsset(part.CreatorID, material, part); foreach (var face in te.FaceTextures) - if (face.MaterialID == id) + if (face != null && face.MaterialID == id) face.MaterialID = newId; } } -- cgit v1.1 From af58631f00b95081dc99f4f75e8ec6b031b8cf2a Mon Sep 17 00:00:00 2001 From: dahlia Date: Mon, 20 Jan 2014 13:57:14 -0800 Subject: rather than converting existing materials to assets, just retrieve them and make them available for viewing. Any new materials added to the scene will become assets. --- .../OptionalModules/Materials/MaterialsModule.cs | 44 ++++++---------------- 1 file changed, 12 insertions(+), 32 deletions(-) (limited to 'OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs') diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index c4bc8a0..afb788b 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -149,12 +149,10 @@ namespace OpenSim.Region.OptionalModules.Materials } /// - /// Searches the part for any legacy materials stored in DynAttrs and converts them to assets, replacing - /// the MaterialIDs in the TextureEntries for the part. - /// Deletes the legacy materials from the part as they are no longer needed. + /// Finds any legacy materials stored in DynAttrs that may exist for this part and add them to 'm_regionMaterials'. /// /// - private void ConvertLegacyMaterialsInPart(SceneObjectPart part) + private void GetLegacyStoredMaterialsInPart(SceneObjectPart part) { if (part.DynAttrs == null) return; @@ -183,10 +181,6 @@ namespace OpenSim.Region.OptionalModules.Materials if (matsArr == null) return; - var te = new Primitive.TextureEntry(part.Shape.TextureEntry, 0, part.Shape.TextureEntry.Length); - if (te == null) - return; - foreach (OSD elemOsd in matsArr) { if (elemOsd != null && elemOsd is OSDMap) @@ -194,32 +188,18 @@ namespace OpenSim.Region.OptionalModules.Materials OSDMap matMap = elemOsd as OSDMap; if (matMap.ContainsKey("ID") && matMap.ContainsKey("Material")) { - UUID id = matMap["ID"].AsUUID(); - OSDMap material = (OSDMap)matMap["Material"]; - bool used = false; - - foreach (var face in te.FaceTextures) - if (face != null && face.MaterialID == id) - used = true; - - if (used) - { // store legacy material in new asset format, and update the part texture entry with the new hashed UUID - - var newId = StoreMaterialAsAsset(part.CreatorID, material, part); - foreach (var face in te.FaceTextures) - if (face != null && face.MaterialID == id) - face.MaterialID = newId; + try + { + lock (m_regionMaterials) + m_regionMaterials[matMap["ID"].AsUUID()] = (OSDMap)matMap["Material"]; + } + catch (Exception e) + { + m_log.Warn("[Materials]: exception decoding persisted legacy material: " + e.ToString()); } } } } - - part.Shape.TextureEntry = te.GetBytes(); - part.ParentGroup.HasGroupChanged = true; - part.ScheduleFullUpdate(); - - lock (part.DynAttrs) - part.DynAttrs.RemoveStore("OpenSim", "Materials"); } /// @@ -230,12 +210,12 @@ namespace OpenSim.Region.OptionalModules.Materials if (part.Shape == null) return; - ConvertLegacyMaterialsInPart(part); - var te = new Primitive.TextureEntry(part.Shape.TextureEntry, 0, part.Shape.TextureEntry.Length); if (te == null) return; + GetLegacyStoredMaterialsInPart(part); + GetStoredMaterialInFace(part, te.DefaultTexture); foreach (Primitive.TextureEntryFace face in te.FaceTextures) -- cgit v1.1