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("\r\n");
public static readonly byte[] XMLkeyMaterialFail = osUTF8.GetASCIIBytes("\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);
+ }
}
}