Flatening Arrays is pain but custom classes break things. Any sugesstions?

I am currently in the middle of making A foliage system and its basically done at this point. its a little bit messy but its just a prototype

[Serializable]
public class FoliageAssetItem {
public GameObject ColiderPrefab;

public byte Priority = 255;

public Mesh LOD0;
public Material[] LOD0Mat;

public Mesh LOD1;
public Material[] LOD1Mat;

public Mesh LOD2;
public Material[] LOD2Mat;

}

public class Foliage_Helper
{
public DataList DesiredChunks;

//Temporary
public static bool LoadJson(String data, DataList FoliageChunks) {

    DataToken FoliageDataToken;
    if (VRCJson.TryDeserializeFromJson(data, out FoliageDataToken))
    {
        DataDictionary FoliageDataDict = FoliageDataToken.DataDictionary;

        if (!FoliageDataDict.TryGetValue("Chunks", out DataToken ChunkList)) return false;

        FoliageChunks = ChunkList.DataList;
        return true;
    }
    else
    {
        return false;
        
    }
}

// Temporary
public static void LoadFoliage(TextAsset foliageData, DataList ChunkData)
{
    String FoliageJson = foliageData.text;

    if (!LoadJson(FoliageJson, ChunkData)) {
        Debug.LogError("Failed to parse foliage data");
    }
}

public static Vector3 GetVec3FromList(DataList Vector) {
    return new Vector3(Vector[0].Float, Vector[1].Float, Vector[2].Float);
}

public static string FormatVec2int(Vector2Int vector) {
    return vector.x.ToString() + "," + vector.y.ToString();
}


public static DataList BuildDesiredChunkList(int Depth) {
    DataList DesiredList = new DataList();

    for (int radius = 0;  radius <= Depth; radius++)
    {
        for (int x = -radius; x <= radius; x++)
        {
            for (int y = -radius; y <= radius; y++)
            {
                bool edge = Mathf.Abs(x) == radius || Mathf.Abs(y) == radius;

                if (!edge)
                    continue;

                DataDictionary position = new DataDictionary();
                position.Add("X", x);
                position.Add("Y", y);

                DesiredList.Add(position);
            }
        }
    }

    return DesiredList;
}

//Needs to be re written for byte reading
public static DataDictionary GetChunkData(Vector2Int chunkPosition, DataList FoliageChunks)
{
    for (int i = 0; i < FoliageChunks.Count; i++)
    {
        DataDictionary ChunkData = FoliageChunks[i].DataDictionary;
        if (!ChunkData.TryGetValue("ChunkX", out DataToken chunkXDT)) { Debug.LogError("Failure to get Chunk X"); return null; }
        if (chunkXDT.Int == chunkPosition.x)
        {
            if (!ChunkData.TryGetValue("ChunkY", out DataToken chunkYDT)) { Debug.LogError("Failure to get Chunk y"); return null; }
            if (chunkYDT.Int == chunkPosition.y) return ChunkData;
        }
    }
    return null;
}

public static Matrix4x4[][] BuildChunkData(DataList ChunkData, byte TreeTypes) {
    if (ChunkData == null) { return null; }

    Matrix4x4[][] TreeItemList = new Matrix4x4[TreeTypes][];

    ushort[] TypeCount = new ushort[TreeTypes];

    //Count Types
    for (int Tree = 0; Tree < ChunkData.Count; Tree++) {
        DataDictionary TreeItem = ChunkData[Tree].DataDictionary;
        TreeItem.TryGetValue("TreeType", out DataToken TreeType);

        TypeCount[TreeType.Int]++;
    }

    // Allocate Types
    int ItemIndx = 0;
    foreach (ushort item in TypeCount) {
        TreeItemList[ItemIndx] = new Matrix4x4[item];
        ItemIndx++;
    }

    ushort[] TypeInsertions = new ushort[TreeTypes];

    // Create Matrixis
    for (int Tree = 0; Tree < ChunkData.Count; Tree++)
    {
        DataDictionary TreeItem = ChunkData[Tree].DataDictionary;
        TreeItem.TryGetValue("TreeType", out DataToken TreeType);

        TreeItem.TryGetValue("Position", out DataToken PositionDict);
        Vector3 Position = GetVec3FromList(PositionDict.DataList);
        TreeItem.TryGetValue("Rotation", out DataToken RotationDict);
        Vector3 Rotation = GetVec3FromList(RotationDict.DataList);
        TreeItem.TryGetValue("Scale", out DataToken Scale);

        TreeItemList[TreeType.Int][TypeInsertions[TreeType.Int]] = Matrix4x4.TRS(Position, Quaternion.Euler(Rotation), Vector3.one * Scale.Float);
        TypeInsertions[TreeType.Int]++;
    }

    return TreeItemList;
}

public static bool ChunkShouldBeLoaded(Vector2Int PlayerChunk ,Vector2Int ChunkPosition, int ChunkRadius) {
    Vector2Int CheckingChunk = ChunkPosition - PlayerChunk;
    return Mathf.Abs(CheckingChunk.x) <= ChunkRadius && Mathf.Abs(CheckingChunk.y) <= ChunkRadius;
}

}

public class Foliage_Manager : UdonSharpBehaviour
{
DataList TreeData;
DataList LoadOrder;
DataList LoadMask;
DataDictionary LoadedChunks;

VRCPlayerApi localPlayer;

public FoliageAssetItem[] TreeAssets;

public byte ChunkSize = 16;

public byte ChunksPerTick = 5;

public int LOD0Radius = 100;
public int LOD1Radius = 250;
public int LOD2Radius = 500;


public TextAsset TerrainTrees = null;

// 1: Chunk 2: TreeType 3 : TreeMatrix
Matrix4x4[][][] TreeRenderMatrix;

private void Start()
{
    
    Foliage_Helper.LoadFoliage(TerrainTrees, TreeData);
    LoadOrder =  Foliage_Helper.BuildDesiredChunkList(LOD2Radius / ChunkSize);

    LoadMask = new DataList();
    LoadedChunks = new DataDictionary();
    localPlayer = Networking.LocalPlayer;

    TreeRenderMatrix = new Matrix4x4[LOD2Radius / ChunkSize][][];
}

Vector2Int LastPlayerChunk = Vector2Int.zero;

private void FixedUpdate()
{
    Vector3 playerPosition = localPlayer.GetPosition();

    //Difference check
    Vector2Int PlayerChunkPosition = new Vector2Int(Mathf.CeilToInt(playerPosition.x / ChunkSize), Mathf.CeilToInt(playerPosition.z / ChunkSize));

    if (!PlayerChunkPosition.Equals(LastPlayerChunk)) {
        LastPlayerChunk = PlayerChunkPosition;

        Debug.Log("Player entered new chunk");
        Debug.Log(LastPlayerChunk);

        //Build the chunk mask for the new layers
        LoadMask.Clear();
        for (int i = 0; i < LoadOrder.Count; i++) {
            DataDictionary chunkPos = LoadOrder[i].DataDictionary;
            
            LoadMask.Add(chunkPos);
        }
    }

    //Loading and cacheing results
    int ItemsLoadedThisTick = 0;
    while (ItemsLoadedThisTick < ChunksPerTick) {
        // Getting an unloaded chunk
        int Chunk = 0;

        for (int chunk = 0; chunk < LoadMask.Count; chunk++) {
            DataDictionary ChunkPos = LoadMask[chunk].DataDictionary;
            ChunkPos.TryGetValue("X", out DataToken XToken);
            ChunkPos.TryGetValue("Y", out DataToken YToken);
            Vector2Int ChunkPosition = new Vector2Int(XToken.Int, YToken.Int);

            if (!LoadedChunks.ContainsKey(Foliage_Helper.FormatVec2int(ChunkPosition))) 
            {
                Foliage_Helper.GetChunkData(ChunkPosition, TreeData).TryGetValue("Trees", out DataToken Dt);
                Matrix4x4[][] mat = Foliage_Helper.BuildChunkData(Dt.DataList, (byte)TreeAssets.Length);

                bool Loaded = false;
                // Check if any of the chunks are null
                for (int i = 0; i < LOD2Radius / ChunkSize; i++) 
                {
                    if (TreeRenderMatrix[i] == null) {
                        //LoadedChunks.TryGetValue(Foliage_Helper.FormatVec2int(ChunkPosition), out DataToken index);
                        TreeRenderMatrix[i] = mat;
                        LoadedChunks.Add(Foliage_Helper.FormatVec2int(ChunkPosition), i);
                        Loaded = true;
                        break;
                    }
                }

                if (!Loaded) {
                    //Find an empty chunk if none are availible
                    for (int FindEmptyChunk = 0; FindEmptyChunk < TreeRenderMatrix.Length; FindEmptyChunk++)
                    {
                        DataList Keys = LoadedChunks.GetKeys();
                        string ChunkCoordsString = Keys[FindEmptyChunk].String;
                        string[] SplitItems = ChunkCoordsString.Split(",");

                        Vector2Int ChunkVector = new Vector2Int(int.Parse(SplitItems[0]), int.Parse(SplitItems[1]));

                        if (!Foliage_Helper.ChunkShouldBeLoaded(PlayerChunkPosition, ChunkVector, LOD2Radius / ChunkSize))
                        {
                            LoadedChunks.TryGetValue(ChunkCoordsString, out DataToken index);
                            TreeRenderMatrix[index.Int] = mat; // This needs to go in there to overwrite it
                            LoadedChunks.Add(Foliage_Helper.FormatVec2int(ChunkPosition), index.Int);
                            LoadedChunks.Remove(ChunkCoordsString);
                            break;
                        }
                    }
                }
                
            }
            
        }
        ItemsLoadedThisTick++;
    }
}

private void Update()
{
    //Render
    for (int Chunks = 0; Chunks < TreeRenderMatrix.Length; Chunks++) {
        if (TreeRenderMatrix[Chunks] == null) { continue; }
        for (int Trees = 0; Trees < TreeAssets.Length; Trees++)
        {
            //FoliageAssetItem item = TreeAssets[Trees];
            //Mesh TreeMesh = item.LOD0;
            //Material[] mats = item.LOD0Mat;

            //for (int submesh = 0; submesh < TreeMesh.subMeshCount; submesh++) {
             //   VRCGraphics.DrawMeshInstanced(TreeMesh, submesh, mats[submesh], TreeRenderMatrix[Chunks][Trees]);
            //}
        }
    }
}

}

The thing is this system is broken entirely because of one issue. in the final few lines of this code are these two lines

Mesh TreeMesh = item.LOD0;
Material[] mats = item.LOD0Mat;

These lines are basically taking my serializable class and reading the data from them such as lod mesh and materials for the draw func. I have done it this way so i don’t have to deal with flattened arrays that are really hard to work with especially when i am planning to have multiple types of object in this list. I would really like to not flatten this but if that’s the only way. i have a feature request for udon :l

I have tryed so many different things. It was originaly a scriptable object then it was a private class then public and nothing has really been able to fix my issue. If you know a fix I wont have to go through the hell of managing 8 different data arrays at the same time.