Use the following network of Unity scripts in order to create me a working SLM mesh file reader/renderer for Unity.

'DecodedMeshData.cs'
using OpenMetaverse;
using OpenMetaverse.Rendering;
using System.Collections.Generic;

namespace CrystalFrost.Assets.Mesh
{
    public class DecodedMesh
    {
        public List<RawMeshData> meshData = new();
		public bool isSkinned = false;
		public UnityEngine.Matrix4x4 bindShapeMatrix = UnityEngine.Matrix4x4.identity;
		public UnityEngine.Matrix4x4 pelvisOffsetMatrix = UnityEngine.Matrix4x4.identity;
		public JointInfo[] joints = null;
		public UUID assetId;
    }
}

'MeshCacheWorker.cs'
using System;
using Microsoft.Extensions.Logging;
using OpenMetaverse;
using OpenMetaverse.Assets;
using System.IO;
using CrystalFrost.Client.Credentials;
using System.Collections.Concurrent;
using CrystalFrost.Config;
using CrystalFrost.Lib;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
using OpenMetaverse.Packets;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace CrystalFrost.Assets.Mesh
{
	public interface IMeshCacheWorker : IDisposable { }

	public class MeshCacheWorker : BackgroundWorker, IMeshCacheWorker
	{
		private readonly MeshConfig _meshConfig;
		private readonly IDownloadedMeshQueue _downloadedMeshQueue;
		private readonly IMeshRequestQueue _meshRequestQueue;
		private readonly IMeshDownloadRequestQueue _downloadRequestQueue;
		private readonly IDownloadedMeshCacheQueue _downloadedCacheQueue;
		private readonly IAesEncryptor _encryptor;
		private bool _isCachingAllowed;
		private string _cachePath;

		public MeshCacheWorker(
			ILogger<IMeshCacheWorker> log,
			IProvideShutdownSignal runningIndicator,
			IAesEncryptor aesMeshEncryptor,
			IDownloadedMeshQueue downloaded,
			IMeshDownloadRequestQueue downloadRequestQueue,
			IMeshRequestQueue meshRequestQueue,
			IDownloadedMeshCacheQueue downloadedCache,
			IOptions<MeshConfig> meshConfig)
			: base("MeshCache", 1, log, runningIndicator)
		{
			_meshConfig = meshConfig.Value;
			_encryptor = aesMeshEncryptor;
			_cachePath = _meshConfig.GetCachePath();
			_isCachingAllowed = _meshConfig.isCachingAllowed;
			if (!Directory.Exists(_cachePath))
			{
				Directory.CreateDirectory(_cachePath);
			}

			_downloadedMeshQueue = downloaded;
			_meshRequestQueue = meshRequestQueue;
			_downloadRequestQueue = downloadRequestQueue;
			_downloadedCacheQueue = downloadedCache;

			_meshRequestQueue.ItemEnqueued += WorkItemEnqueued;
			_downloadedCacheQueue.ItemEnqueued += WorkItemEnqueued;
			_meshRequestQueue.ItemDequeued += WorkItemEnqueued;
			_downloadedCacheQueue.ItemDequeued += WorkItemEnqueued;

		}

		private void WorkItemEnqueued(MeshRequest obj)
		{
			CheckForWork();
		}

		protected override Task<bool> DoWork()
		{
			if (!_isCachingAllowed)
			{
				return Task.Run(() =>
					{
						// Just pass through the mesh requests through queues
						bool resultLoad = DoWorkImplPassThroughLoad(); // Request Queue -> Download Request Queue (skip cache)
						bool resultSave = DoWorkImplPassThroughSave(); // Downloaded Cache Queue -> Downloaded Mesh Queue (skip cache)
						return resultLoad || resultSave;
					});
			}
			else
			{
				return Task.Run(() =>
				{
					bool resultLoad = DoWorkImplLoadCache(); // Request Queue -> (cache check) (A - cache exists) -> Downloaded Mesh Queue (skip download) (B - cache miss) -> Download Request Queue
					bool resultSave = DoWorkImplSaveCache(); // Downloaded Cache Queue -> (cache save) -> Downloaded Mesh Queue
					return resultLoad || resultSave;
				});
			}
		}

		private bool DoWorkImplPassThroughLoad()
		{
			if (_meshRequestQueue.Count == 0) return false;
			if (!_meshRequestQueue.TryDequeue(out var request)) return true;
			if (request == null) return true;
			_downloadRequestQueue.Enqueue(request);
			return _meshRequestQueue.Count > 0;
		}

		private bool DoWorkImplPassThroughSave()
		{
			if (_downloadedCacheQueue.Count == 0) return false;
			if (!_downloadedCacheQueue.TryDequeue(out var request)) return true;
			if (request == null) return true;
			_downloadedMeshQueue.Enqueue(request);
			return _downloadedCacheQueue.Count > 0;
		}

		private bool DoWorkImplLoadCache()
		{
			if (_meshRequestQueue.Count == 0) return false;
			if (!_meshRequestQueue.TryDequeue(out var request)) { return true; }
			if (request == null) return true;
			var cachePath = Path.Combine(_cachePath, request.UUID.ToString() + ".asset");
			if (!File.Exists(cachePath)) // mesh is not cached, pass it to download queue
			{
				_downloadRequestQueue.Enqueue(request);
			}
			else // load cached mesh
			{
				using (var stream = File.OpenRead(cachePath))
				{
					var encryptedData = new byte[stream.Length];
					stream.Read(encryptedData, 0, encryptedData.Length);
					var decryptedData = _encryptor.Decrypt(encryptedData);
					request.AssetMesh = new AssetMesh(request.UUID, decryptedData);
					_downloadedMeshQueue.Enqueue(request);
				}
			}
			return _meshRequestQueue.Count > 0;
		}

		private bool DoWorkImplSaveCache()
		{
			if (_downloadedCacheQueue.Count == 0) return false;
			if (!_downloadedCacheQueue.TryDequeue(out var request)) return true;
			if (request == null) return true;
			var cachePath = Path.Combine(_cachePath, request.UUID.ToString() + ".asset");
			if (!File.Exists(cachePath))
			{
				using (var stream = File.Create(cachePath))
				{
					var encryptedData = _encryptor.Encrypt(request.AssetMesh.AssetData);
					stream.Write(encryptedData, 0, encryptedData.Length);
				}
			}
			_downloadedMeshQueue.Enqueue(request);
			return _downloadedCacheQueue.Count > 0;
		}


		protected override bool OutputIsBacklogged()
		{
			//return _downloaded.Count > _meshConfig.MaxDownloadedMeshes;
			return false; // Temporary measure for now
		}

		protected override void ShuttingDown()
		{
			base.ShuttingDown();
		}
	}
}

'MeshDecoder.cs'
using OpenMetaverse.Rendering;
using OpenMetaverse;
using System;
using UnityEngine;
using Vector3 = UnityEngine.Vector3;
using CrystalFrost.Extensions;

namespace CrystalFrost.Assets.Mesh
{
    public interface IMeshDecoder
    {
        void Decode(MeshRequest request);
    }

    public class MeshDecoder : IMeshDecoder
    {
        private readonly IDecodedMeshQueue _readyMeshQueue;

        public MeshDecoder(IDecodedMeshQueue readyMeshQueue)
        {
            _readyMeshQueue = readyMeshQueue;
        }

        public void Decode(MeshRequest request)
        {
			TranscodeFacetedMeshAtDetailLevel(request, DetailLevel.Highest);
		}

		private void TranscodeFacetedMeshAtDetailLevel(MeshRequest request, DetailLevel detailLevel)
        {
            var prim = request.Primitive;
            var assetMesh = request.AssetMesh;

			if (!RiggedMesh.TryDecodeFromAsset(prim, assetMesh, detailLevel, out RiggedMesh fmesh))
			{
				Debug.LogWarning($"Unable to decode {detailLevel} detail mesh UUID: {request.UUID}");
				return;
			}

            request.DecodedMesh = new();
			request.DecodedMesh.assetId = request.UUID;
			request.DecodedMesh.joints = fmesh.Joints;
			request.DecodedMesh.bindShapeMatrix = fmesh.BindShapeMatrix.ToMatrix4x4();
			request.DecodedMesh.pelvisOffsetMatrix = fmesh.PelvisOffsetMatrix.ToMatrix4x4();
			request.DecodedMesh.isSkinned = fmesh.Joints != null && fmesh.Joints.Length > 0;

            for (var j = 0; j < fmesh.Faces.Count; j++)
            {
                Primitive.TextureEntryFace textureEntryFace = prim.Textures.GetFace((uint)j);
				RawMeshData rmd = null;

				// rmd = fmesh.Faces[j].ToRawMeshData()
				if (fmesh.IsSkinned)
				{
					rmd = fmesh.Faces[j].ToRawMeshData(fmesh.JointInfluences[j], fmesh.Joints);
				}
				else
				{
					rmd = fmesh.Faces[j].ToRawMeshData();
				}


				float cosineAngle = (float)Math.Cos(textureEntryFace.Rotation * Mathf.Deg2Rad);
                float sinAngle = (float)Math.Sin(textureEntryFace.Rotation * Mathf.Deg2Rad);

                for (var i = 0; i < rmd.uvs.Length; i++)
                {
                    float repeatU = textureEntryFace.RepeatU;
                    float repeatV = textureEntryFace.RepeatV;
                    float tX = rmd.uvs[i].x - 0.5f;
                    float tY = rmd.uvs[i].y - 0.5f;

                    if (textureEntryFace.TexMapType == MappingType.Planar)
                    {
                        Vector3 binormal;
                        float d = Vector3.Dot(rmd.normals[i], Vector3.right);
                        if (d >= 0.5f || d <= -0.5f)
                        {
                            binormal = Vector3.forward;
                            if (rmd.normals[i].x < 0f) binormal *= -1;
                        }
                        else
                        {
                            binormal = Vector3.right;
                            if (rmd.normals[i].z > 0f) binormal *= -1;
                        }
                        Vector3 tangent = Vector3.Cross(binormal, rmd.normals[i]);//binormal % rmd.normals[i];
                        var primScale = prim.Scale.ToUnity();
                        var scaledPos = Vector3.Scale(rmd.vertices[i], primScale);

                        rmd.uvs[i].x = 1f + (Vector3.Dot(binormal, scaledPos) * 2f - 0.5f);
                        rmd.uvs[i].y = -(Vector3.Dot(tangent, scaledPos) * 2f - 0.5f);
                    }

                    rmd.uvs[i].x = (tX * cosineAngle + tY * sinAngle) * repeatU + textureEntryFace.OffsetU + 0.5f;
                    rmd.uvs[i].y = (-tX * sinAngle + tY * cosineAngle) * repeatV + (1f - textureEntryFace.OffsetV) + 0.5f;
                }
                request.DecodedMesh.meshData.Add(rmd);
            }
            _readyMeshQueue.Enqueue(request);
        }
    }
}

'MeshDecodeWorker.cs'
using CrystalFrost.Config;
using CrystalFrost.Lib;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;

namespace CrystalFrost.Assets.Mesh
{
	public interface IMeshDecodeWorker : IDisposable
	{

	}

	public class MeshDecodeWorker : BackgroundWorker, IMeshDecodeWorker
	{
		private readonly MeshConfig _meshConfig;
		private readonly IDownloadedMeshQueue _downloadedMeshQueue;
		private readonly IDecodedMeshQueue _readyMeshQueue;
		private readonly IMeshDecoder _meshDecoder;

		public MeshDecodeWorker(
			ILogger<MeshDecodeWorker> log,
			IProvideShutdownSignal runningIndicator,
			IDownloadedMeshQueue downloadedMeshQueue,
			IDecodedMeshQueue readyMeshQueue,
			IMeshDecoder meshDecoder,
			IOptions<MeshConfig> meshConfig)
			: base("MeshDecode", 0, log, runningIndicator)
		{
			_meshConfig = meshConfig.Value;
			_downloadedMeshQueue = downloadedMeshQueue;
			_downloadedMeshQueue.ItemEnqueued += DownloadedMeshQueue_ItemEnqueued;
			_readyMeshQueue = readyMeshQueue;
			_readyMeshQueue.ItemDequeued += ReadyMeshQueue_ItemDequeued;
			_meshDecoder = meshDecoder;
		}

		private void ReadyMeshQueue_ItemDequeued(MeshRequest obj)
		{
			CheckForWork();
		}

		private void DownloadedMeshQueue_ItemEnqueued(MeshRequest obj)
		{
			CheckForWork();
		}

		protected override Task<bool> DoWork()
		{
			return Task.Run(() => DoWorkImpl());
		}

		private bool DoWorkImpl()
		{
			if (_downloadedMeshQueue.Count == 0) return false;
			if (!_downloadedMeshQueue.TryDequeue(out var request)) return true;
			if (request is null) return true;
			// decode something
			_meshDecoder.Decode(request);
			return _downloadedMeshQueue.Count > 0;
		}

		protected override bool OutputIsBacklogged()
		{
			return _readyMeshQueue.Count > _meshConfig.MaxReadyMeshes;
		}

		public override void Dispose()
		{
			_downloadedMeshQueue.ItemEnqueued -= DownloadedMeshQueue_ItemEnqueued;
			_readyMeshQueue.ItemDequeued -= ReadyMeshQueue_ItemDequeued;
			base.Dispose();
		}
	}
}

'MeshExtensions.cs'
using CrystalFrost.Extensions;
using OpenMetaverse.Rendering;
using System;
using System.Collections.Generic;
using System.Security.Policy;
using UnityEngine;

namespace CrystalFrost.Assets.Mesh
{
	public static class MeshExtensions
	{
		public static UnityEngine.Mesh ReverseWind(this UnityEngine.Mesh mesh)
		{
			var indices = mesh.triangles;
			var triangleCount = indices.Length / 3;
			for (var i = 0; i < triangleCount; i++)
			{
#pragma warning disable IDE0180 // using tuples to swap values is not more readable or more efficient
				var tmp = indices[i * 3];
#pragma warning restore IDE0180
				indices[i * 3] = indices[i * 3 + 1];
				indices[i * 3 + 1] = tmp;
			}
			mesh.triangles = indices;
			return mesh;
		}

		public static UnityEngine.Mesh FlipNormals(this UnityEngine.Mesh mesh)
		{
			var normals = mesh.normals;
			for (var n = 0; n < normals.Length; n++)
			{
				normals[n] = -normals[n];
			}
			mesh.normals = normals;

			return mesh;
		}

		/// <summary>
		/// Converts a OpenMetaverse.Reandering.Face into RawMeshData.
		/// </summary>
		public static RawMeshData ToRawMeshData(this Face face)
		{
			var indices = face.Indices.ToArray();
			var count = face.Vertices.Count;
			var vertices = new Vector3[count];
			var normals = new Vector3[count];
			var uvs = new Vector2[count];

			// could Parallel.For speed this up?
			for (var i = 0; i < count; i++)
			{
				var vert = face.Vertices[i];
				vertices[i] = vert.Position.ToUnity();
				normals[i] = vert.Normal.ToUnity();
				uvs[i] = vert.TexCoord.ToUnity();
			}

			return new RawMeshData(vertices, uvs, normals, indices);
		}

		public static RawMeshData ToRawMeshData(this Face face, List<JointInfluence[]> jointInfluences, JointInfo[] joints)
		{
			var rmd = face.ToRawMeshData();

			if (jointInfluences == null) return rmd;

			var count = face.Vertices.Count;
			var weights = new BoneWeight[count];

			for (var i = 0; i < count; i++)
			{
				var influences = jointInfluences[i];
				var weight = weights[i];
				weight.weight0 = weight.weight1 = weight.weight2 = weight.weight3 = 0.0f;

				for (int j = 0, k = 0; j < influences.Length; j++)
				{
					var influence = influences[j];
					var jointIndex = influence.JointIndex;
					var weightValue = influence.WeightValue;

					//if (!isRegularBone(jointIndex)) continue;

					if (k == 0)
					{
						weight.boneIndex0 = jointIndex;
						weight.weight0 = weightValue;
					}
					else if (k == 1)
					{
						weight.boneIndex1 = jointIndex;
						weight.weight1 = weightValue;
					}
					else if (k == 2)
					{
						weight.boneIndex2 = jointIndex;
						weight.weight2 = weightValue;
					}
					else if (k == 3)
					{
						weight.boneIndex3 = jointIndex;
						weight.weight3 = weightValue;
					}
					k++;
				}

				float sum = weight.weight0 + weight.weight1 + weight.weight2 + weight.weight3;
				if (sum > 0.0f)
				{
					weight.weight0 /= sum;
					weight.weight1 /= sum;
					weight.weight2 /= sum;
					weight.weight3 /= sum;
				}

				weights[i] = weight;				
			}

			rmd.boneWeights = weights;
			rmd.isSkinned = true;

			return rmd;
		}

		/// <summary>
		/// Converts a OpenMetaverse.Reandering.Face into RawMeshData.
		/// </summary>
		[Obsolete("Decode FacetedMesh.Face[face] directly.")]
		public static RawMeshData ToRawMeshData(FacetedMesh fmesh, int face)
		{
			return ToRawMeshData(fmesh.Faces[face]);
		}

		public static UnityEngine.Mesh[] ToUnityMeshArray(this List<RawMeshData> meshData)
		{
			// I wonder if linq is comparable in speed?
			// return meshData.Select(rmd => rmd.ToUnityMesh()).ToArray();
			var result = new UnityEngine.Mesh[meshData.Count];
			for (var i = 0; i < result.Length; i++)
			{
				result[i] = meshData[i].ToUnityMesh();
			}
			return result;
		}

		public static UnityEngine.Mesh ToUnityMesh(this RawMeshData rmd)
		{
			var result = new UnityEngine.Mesh
			{
				vertices = rmd.vertices,
				uv = rmd.uvs,
				normals = rmd.normals,
			};

			if (rmd.isSkinned)
			{
				result.boneWeights = rmd.boneWeights;
				// result.bindposes = rmd.bindPoses;
			}

			result.SetIndices(rmd.indices, MeshTopology.Triangles, 0);
			result = result.ReverseWind();
			result.RecalculateBounds();
			return result;
		}
	}
}

'RawMeshData.cs'
using UnityEngine;

namespace CrystalFrost.Assets.Mesh
{
    /// <summary>
    /// contains basic mesh data:
    /// Arrays of verticies, uvs, normals, and incdices
    /// </summary>
    public class RawMeshData
    {
        public Vector3[] vertices;
        public Vector2[] uvs;
        public Vector3[] normals;
        public ushort[] indices;
		public BoneWeight[] boneWeights;
		public bool isSkinned = false;

        public RawMeshData(Vector3[] vertices, Vector2[] uvs, Vector3[] normals, ushort[] indices)
        {
            this.vertices = vertices;
            this.uvs = uvs;
            this.normals = normals;
            this.indices = indices;
        }
    }
}