Мигает mesh при мультипотоке c# unity 2020

При изменении ландшафта редактируется mesh через еще один поток (потому что 12ms такое себе) И вот вроде без многопотока все было нормально, но когда добавил вычисление на новом потоке. Mesh начинает мигать.

Gif: https://imgur.com/Sz9Qoac (прямо тут не могу вставить потому что >2mb)

Скрипт:

using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

/*Thanks to: b3agz*/
/* https://www.youtube.com/channel/UC3Ej26l1kXBPIq0fEEMwxQw */

public class Chunk
{
    public GameObject chunkObject;
    MeshFilter meshFilter;
    MeshCollider meshCollider;
    MeshRenderer meshRenderer;

    WorldGenerator wg;

    bool clampPosition = true;

    Thread aThread;
    public bool DataComplete = false;
    [SerializeField] bool threadWork = false;
    [SerializeField] bool threadQueue = false;

    Vector3Int chunkPosition;

    float[,,] terrainMap;

    List<Vector3> vertices = new List<Vector3>();
    List<int> triangles = new List<int>();

    int width { get { return CubeData.ChunkWidth; } }
    int height { get { return CubeData.ChunkHeight; } }
    float terrainSurface { get { return CubeData.terrainSurface; } }

    public Chunk(Vector3Int _position)
    {
        chunkObject = new GameObject();
        chunkObject.name = string.Format("Chunk {0}, {1}", _position.x, _position.z);
        chunkPosition = _position;
        chunkObject.transform.position = chunkPosition;

        meshFilter = chunkObject.AddComponent<MeshFilter>();
        meshCollider = chunkObject.AddComponent<MeshCollider>();
        meshRenderer = chunkObject.AddComponent<MeshRenderer>();
        meshRenderer.material = Resources.Load<Material>("Materials/NoTextureMaterial");

        wg = GameObject.FindGameObjectWithTag("World").GetComponent<WorldGenerator>();

        chunkObject.transform.tag = "Terrain";
        terrainMap = new float[width + 1, height + 1, width + 1];
        ClearMeshData();

        PopulateTerrainMap();

        CreateMeshData();
    }

    void CreateMeshData()
    {
        ClearMeshData();

        aThread = new Thread(() => BuildMeshDataForMesh());
        aThread.Start();

        BuildMesh();

    }
    void BuildMeshDataForMesh()
    {
        threadWork = true;

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                for (int z = 0; z < width; z++)
                {
                    MarchCube(new Vector3Int(x, y, z));
                }
            }
        }
        threadWork = false;
        DataComplete = true;
    }

    void PopulateTerrainMap()
    {

        // The data points for terrain are stored at the corners of our "cubes", so the terrainMap needs to be 1 larger
        // than the width/height of our mesh.
        for (int x = 0; x < width + 1; x++)
        {
            for (int z = 0; z < width + 1; z++)
            {
                for (int y = 0; y < height + 1; y++)
                {

                    // Get a terrain height using regular old Perlin noise.
                    float thisHeight = CubeData.GetTerrainHeight(x + chunkPosition.x, z + chunkPosition.z);

                    // Set the value of this point in the terrainMap.
                    terrainMap[x, y, z] = (float)y - thisHeight;
                }
            }
        }
    }

    void MarchCube(Vector3Int position)
    {
        // Sample terrain values at each corner of the cube.
        float[] cube = new float[8];
        for (int i = 0; i < 8; i++)
        {

            cube[i] = SampleTerrain(position + CubeData.CornerTable[i]);
        }

        // Get the configuration index of this cube.
        int configIndex = GetCubeConfiguration(cube);

        // If the configuration of this cube is 0 or 255 (completely inside the terrain or completely outside of it) we don't need to do anything.
        if (configIndex == 0 || configIndex == 255)
            return;

        // Loop through the triangles. There are never more than 5 triangles to a cube and only three vertices to a triangle.
        int edgeIndex = 0;
        for (int i = 0; i < 5; i++)
        {
            for (int p = 0; p < 3; p++)
            {

                // Get the current indice. We increment triangleIndex through each loop.
                int indice = CubeData.TriangleTable[configIndex, edgeIndex];

                // If the current edgeIndex is -1, there are no more indices and we can exit the function.
                if (indice == -1)
                    return;

                // Get the vertices for the start and end of this edge.
                Vector3 vert1 = position + CubeData.CornerTable[CubeData.EdgeIndexes[indice, 0]];
                Vector3 vert2 = position + CubeData.CornerTable[CubeData.EdgeIndexes[indice, 1]];

                Vector3 vertPosition;
                {

                    // Get the terrain values at either end of our current edge from the cube array created above.
                    float vert1Sample = cube[CubeData.EdgeIndexes[indice, 0]];
                    float vert2Sample = cube[CubeData.EdgeIndexes[indice, 1]];

                    // Calculate the difference between the terrain values.
                    float difference = vert2Sample - vert1Sample;

                    // If the difference is 0, then the terrain passes through the middle.
                    if (difference == 0)
                        difference = terrainSurface;
                    else
                        difference = (terrainSurface - vert1Sample) / difference;

                    // Calculate the point along the edge that passes through.
                    vertPosition = vert1 + ((vert2 - vert1) * difference);


                }

                // Add to our vertices and triangles list and incremement the edgeIndex.
                vertices.Add(vertPosition);
                triangles.Add(vertices.Count - 1);

                edgeIndex++;
            }
        }
    }
    int GetCubeConfiguration(float[] cube)
    {

        // Starting with a configuration of zero, loop through each point in the cube and check if it is below the terrain surface.
        int configurationIndex = 0;
        for (int i = 0; i < 8; i++)
        {

            // If it is, use bit-magic to the set the corresponding bit to 1. So if only the 3rd point in the cube was below
            // the surface, the bit would look like 00100000, which represents the integer value 32.
            if (cube[i] > terrainSurface)
                configurationIndex |= 1 << i;

        }

        return configurationIndex;

    }

    public void PlaceTerrain(Vector3 pos, int brushSize)
    {
        if (!aThread.IsAlive)
        {
            for (int x = 0; x < brushSize; x++)
            {
                for (int y = 0; y < brushSize; y++)
                {
                    for (int z = 0; z < brushSize; z++)
                    {
                        float halfSize = brushSize / 2;
                        Vector3 dropPos = new Vector3((pos.x + x) - halfSize, (pos.y + y) - halfSize, (pos.z + z) - halfSize);

                        Vector3Int v3Int = new Vector3Int(Mathf.CeilToInt(dropPos.x), Mathf.CeilToInt(dropPos.y), Mathf.CeilToInt(dropPos.z));
                        v3Int -= chunkPosition;
                        dropPos -= chunkPosition;

                        if (clampPosition)
                        {
                            if (dropPos.x + 1 >= 0 && dropPos.x <= terrainMap.GetLength(0) - 1 && dropPos.y + 1 >= 0 && dropPos.y <= terrainMap.GetLength(1) - 1 && dropPos.z + 1 >= 0 && dropPos.z <= terrainMap.GetLength(2) - 1)
                            {
                                terrainMap[v3Int.x, v3Int.y, v3Int.z] = 0f;
                            }
                            else
                            {
                                if (dropPos.x + 1 >= 0 && dropPos.x <= terrainMap.GetLength(0) - 1)
                                {

                                }
                            }
                        }
                        else
                        {
                            terrainMap[v3Int.x, v3Int.y, v3Int.z] = 0f;
                        }

                    }
                }
            }
            CreateMeshData();
        }
    }
    public void RemoveTerrain(Vector3 pos, int brushSize)
    {
        if (!aThread.IsAlive)
        {
            for (int x = 0; x < brushSize; x++)
            {
                for (int y = 0; y < brushSize; y++)
                {
                    for (int z = 0; z < brushSize; z++)
                    {
                        float halfSize = brushSize / 2;
                        Vector3 dropPos = new Vector3((pos.x + x) - halfSize, (pos.y + y) - halfSize, (pos.z + z) - halfSize);

                        Vector3Int v3Int = new Vector3Int(Mathf.CeilToInt(dropPos.x), Mathf.CeilToInt(dropPos.y), Mathf.CeilToInt(dropPos.z));
                        v3Int -= chunkPosition;
                        dropPos -= chunkPosition;

                        if (clampPosition)
                        {
                            if (dropPos.x + 1 >= 0 && dropPos.x <= terrainMap.GetLength(0) - 1)
                            {
                                if (dropPos.y + 1 >= 0 && dropPos.y <= terrainMap.GetLength(1) - 1)
                                {
                                    if (dropPos.z + 1 >= 0 && dropPos.z <= terrainMap.GetLength(2) - 1)
                                    {
                                        terrainMap[v3Int.x, v3Int.y, v3Int.z] = 1f;
                                    }
                                }
                            }
                        }
                        else
                        {
                            terrainMap[v3Int.x, v3Int.y, v3Int.z] = 1f;
                        }

                    }
                }
            }
            CreateMeshData();
        }
    }

    float SampleTerrain(Vector3Int point)
    {
        return terrainMap[point.x, point.y, point.z];
    }

    void ClearMeshData()
    {
        vertices = new List<Vector3>();
        triangles = new List<int>();
    }
    public void BuildMesh()
    {
        if(vertices != null && triangles != null)
        {
            if (triangles.Count % 3 == 0)
            {
                Mesh mesh = new Mesh();

                Vector3[] vertNew = new Vector3[vertices.Count];
                int[] triNew = new int[triangles.Count];

                Array.Copy(vertices.ToArray(), vertNew, vertices.Count);
                Array.Copy(triangles.ToArray(), triNew, triangles.Count);

                mesh.vertices = vertNew;
                mesh.triangles = triNew;

                mesh.RecalculateNormals();
                meshFilter.mesh = mesh;
                meshCollider.sharedMesh = mesh;


            }
            else
            {
                LogWriter.Log("ERROR: Chunk triangles % 3 == 0");
            }
        }
        else
        {
            LogWriter.Log("ERROR: Chunk triangles or verices == null");
        }
    }

    public void ConstUpdate()
    {
        if (threadQueue)
        {
            if (!threadWork)
            {
                BuildMesh();
                threadQueue = false;
            }
        }
    }

    public void CallChunkUpdate()
    {
        threadQueue = true;
    }
}

Ответы (0 шт):