diff --git a/OpenSim/Region/Framework/Interfaces/IMaterialsModule.cs b/OpenSim/Region/Framework/Interfaces/IMaterialsModule.cs index 837806338f..3d3617d034 100644 --- a/OpenSim/Region/Framework/Interfaces/IMaterialsModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IMaterialsModule.cs @@ -25,8 +25,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -using OpenSim.Region.Framework.Scenes; using OpenMetaverse; +using OpenSim.Region.Framework.Scenes; +using static OpenMetaverse.Primitive.RenderMaterials; namespace OpenSim.Region.Framework.Interfaces { @@ -36,5 +37,7 @@ namespace OpenSim.Region.Framework.Interfaces FaceMaterial GetMaterialCopy(UUID ID); UUID AddNewMaterial(FaceMaterial fm); void RemoveMaterial(UUID id); + bool CleanMaterialOverrides(ref RenderMaterialOverrideEntry[] overrides, int side, bool removeTransforms = false); + bool RemoveMaterialEntry(ref RenderMaterialEntry[] entries, int side); } } diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index 69e6d70805..5f36f5dfe9 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -1025,7 +1025,8 @@ namespace OpenSim.Region.OptionalModules.Materials public static readonly byte[] XMLkeyMaterialSucess = osUTF8.GetASCIIBytes("success1\r\n"); public static readonly byte[] XMLkeyMaterialFail = osUTF8.GetASCIIBytes("success0\r\n"); - private static bool RemoveMaterialEntry(ref RenderMaterialEntry[] entries, int side) + + public bool RemoveMaterialEntry(ref RenderMaterialEntry[] entries, int side) { if (entries is null || entries.Length == 0) return false; @@ -1386,5 +1387,90 @@ namespace OpenSim.Region.OptionalModules.Materials overrides[indx].data = data; return true; } + + public static bool ClearOverrideData(OSDMap facemat, out OSDMap changedmat) + { + if(facemat is not null && facemat.TryGetOSDArray("ti", out OSDArray transforms)) + { + changedmat = new OSDMap() + { + ["ti"] = transforms + }; + return facemat.Count > 1; + } + + changedmat = null; + return facemat is not null; + } + + public bool CleanMaterialOverrides(ref RenderMaterialOverrideEntry[] overrides, int side, bool removeTransforms = false) + { + if (overrides is null || overrides.Length == 0) + return false; + + bool changed = false; + if(removeTransforms && side < 0) + { + overrides = null; + return true; + } + + List newoverrides = new(overrides.Length); + + for(int indx = 0; indx < overrides.Length; indx++) + { + if(side >= 0 && side != overrides[indx].te_index) + { + newoverrides.Add(overrides[indx]); + } + else if (removeTransforms) + { + changed = true; + } + else + { + if(string.IsNullOrEmpty(overrides[indx].data)) + { + changed = true; + continue; + } + + int tiindx = overrides[indx].data.IndexOf("ti"); + if(tiindx < 0) + { + changed = true; + continue; + } + + StringBuilder sb = osStringBuilderCache.Acquire(); + sb.Append("{'"); + int brk = 0; + do + { + char c = overrides[indx].data[tiindx]; + sb.Append(c); + if(c=='[') + brk++; + else if(c==']') + { + brk--; + if(brk == 0) + break; + } + tiindx++; + } + while (tiindx < overrides[indx].data.Length); + + sb.Append('}'); + string newdata = osStringBuilderCache.GetStringAndRelease(sb); + changed = !newdata.Equals(overrides[indx].data); + overrides[indx].data = newdata; + newoverrides.Add(overrides[indx]); + } + } + + overrides = newoverrides.Count > 0 ? [.. newoverrides] : null; + return changed; + } } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 4f10b8de6d..041cbb9be3 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -19612,6 +19612,123 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return 0; } + public void llSetRenderMaterial(LSL_String materialstr, LSL_Integer lsl_face) + { + if(m_materialsModule is null) + return; + + if(string.IsNullOrEmpty(materialstr.m_string)) + { + Error("llSetRenderMaterial", "material \"\" not found"); + return; + } + + int face = lsl_face.value; + bool changed; + + if(UUID.ZeroString.Equals(materialstr.m_string, StringComparison.OrdinalIgnoreCase)) + { + if(m_host.Shape.RenderMaterials is null || m_host.Shape.RenderMaterials.entries is null || m_host.Shape.RenderMaterials.entries.Length == 0) + return; + + changed = m_materialsModule.CleanMaterialOverrides(ref m_host.Shape.RenderMaterials.overrides, face); + if(face == ScriptBaseClass.ALL_SIDES) + { + m_host.Shape.RenderMaterials.entries = null; + changed = true; + } + else + changed |= m_materialsModule.RemoveMaterialEntry(ref m_host.Shape.RenderMaterials.entries, face); + + if(changed) + { + m_host.ParentGroup.HasGroupChanged = true; + m_host.ScheduleUpdate(PrimUpdateFlags.MaterialOvr | PrimUpdateFlags.FullUpdate); + m_host.TriggerScriptChangedEvent(Changed.MATERIAL); + } + return; + } + + UUID matID = ScriptUtils.GetAssetIdFromItemName(m_host, materialstr.m_string, (int)AssetType.Material); + if (matID.IsZero()) + { + if (!UUID.TryParse(materialstr.m_string, out matID) || matID.IsZero()) + { + Error("llSetRenderMaterial", $"material \"{materialstr.m_string}\" not found"); + return; + } + } + + int nsides = GetNumberOfSides(m_host); + if(face >= nsides) + return; + + m_host.Shape.RenderMaterials ??= new(); + m_host.Shape.RenderMaterials.entries ??= new Primitive.RenderMaterials.RenderMaterialEntry[1]; + + changed = m_materialsModule.CleanMaterialOverrides(ref m_host.Shape.RenderMaterials.overrides, face); + if(face == ScriptBaseClass.ALL_SIDES) + { + if(m_host.Shape.RenderMaterials.entries is null || m_host.Shape.RenderMaterials.entries.Length != nsides) + { + m_host.Shape.RenderMaterials.entries = new Primitive.RenderMaterials.RenderMaterialEntry[nsides]; + for (int i = 0; i < m_host.Shape.RenderMaterials.entries.Length; i++) + { + m_host.Shape.RenderMaterials.entries[i] = new() + { + te_index = (byte)i, + id = matID + }; + } + changed = true; + } + else + { + for (int i = 0; i < m_host.Shape.RenderMaterials.entries.Length; i++) + { + if(matID.NotEqual(m_host.Shape.RenderMaterials.entries[i].id)) + { + changed = true; + m_host.Shape.RenderMaterials.entries[i].id = matID; + } + } + } + } + else + { + int indx = 0; + for( ; indx < m_host.Shape.RenderMaterials.entries.Length; indx++) + { + if (m_host.Shape.RenderMaterials.entries[indx].te_index == face) + { + if(matID.NotEqual(m_host.Shape.RenderMaterials.entries[indx].id)) + { + changed = true; + m_host.Shape.RenderMaterials.entries[indx].id = matID; + } + break; + } + } + if(indx == m_host.Shape.RenderMaterials.entries.Length) + { + Array.Resize(ref m_host.Shape.RenderMaterials.entries, m_host.Shape.RenderMaterials.entries.Length + 1); + + m_host.Shape.RenderMaterials.entries[indx] = new() + { + te_index = (byte)face, + id = matID + }; + changed = true; + } + } + if(changed) + { + m_host.ParentGroup.HasGroupChanged = true; + m_host.ScheduleUpdate(PrimUpdateFlags.MaterialOvr | PrimUpdateFlags.FullUpdate); + m_host.TriggerScriptChangedEvent(Changed.MATERIAL); + } + } + public LSL_Vector llWorldPosToHUD(LSL_Vector wp) { if(!m_host.ParentGroup.IsAttachment) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs index 0d7d691d25..689d5ac895 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs @@ -537,5 +537,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces LSL_String llGetRenderMaterial(LSL_Integer face); LSL_Integer llIsLinkGLTFMaterial(LSL_Integer linknum, LSL_Integer face); LSL_Vector llWorldPosToHUD(LSL_Vector WorldPosition); + void llSetRenderMaterial(LSL_String material, LSL_Integer face); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs index 99917316b6..e8c82d09aa 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs @@ -2899,5 +2899,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase { return m_LSL_Functions.llWorldPosToHUD(WorldPosition); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void llSetRenderMaterial(LSL_String material, LSL_Integer face) + { + m_LSL_Functions.llSetRenderMaterial(material, face); + } } }