Search Unity

Proper use of native arrays with Mesh.SetVertices

Discussion in 'Data Oriented Technology Stack' started by Sarkahn, Dec 3, 2019.

  1. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    105
    So I've seen that we can now pass NativeArrays directly in to meshes like:
    Code (CSharp):
    1.  
    2.         mesh = new Mesh();
    3.         verts = new NativeList<float3>(Allocator.Persistent);
    4.         indices = new NativeList<int>(Allocator.Persistent);
    5.         colors = new NativeList<Color32>(Allocator.Persistent);
    6.  
    7.         verts.Add(new float3(0, 0, 0));
    8.         verts.Add(new float3(0, 1, 0));
    9.         verts.Add(new float3(1, 1, 0));
    10.         verts.Add(new float3(1, 0, 0));
    11.  
    12.         indices.Add(0);
    13.         indices.Add(1);
    14.         indices.Add(2);
    15.         indices.Add(0);
    16.         indices.Add(2);
    17.         indices.Add(3);
    18.  
    19.         for (int i = 0; i < 4; ++i)
    20.             colors.Add(Color.blue);
    21.  
    22.         mesh.SetVertices<float3>(verts);
    23.         mesh.SetIndices<int>(indices, MeshTopology.Triangles, 0);
    24.  
    25.        
    26.         mesh.RecalculateBounds();
    27.         mesh.RecalculateNormals();
    28.         mesh.RecalculateTangents();
    I thought that since it's a nativearray that I could then change the verts and it would "automatically" affect the mesh:
    Code (CSharp):
    1.     [ContextMenu("ChangeFace")]
    2.     void ChangeFace()
    3.     {
    4.         for( int i = 0; i < 4; ++i )
    5.             verts[i] = verts[i] - new float3(1, 0, 0);
    6.     }
    But that does nothing on it's own, so I guess I'm misunderstanding something there. I can call Mesh.SetVertices again after the changes and it of course works.

    Do you still need to call Mesh.SetVertices/SetIndices/etc after any changes even if you're using NativeArrays? If so is there any benefit to using NativeArrays instead of Managed Lists other than being able to use the NativeArrays in jobs?
     
  2. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    105
    So based on what I was told from the discord and on testing it seems you still need to call Set* functions on the mesh after you made your changes in the NativeArrays. You can make your changes in a job though:
    Code (CSharp):
    1.     struct AddFaceJob : IJob
    2.     {
    3.         public NativeList<float3> verts;
    4.         public NativeList<int> indices;
    5.         public NativeList<Color32> colors;
    6.  
    7.         public float3 origin;
    8.  
    9.         public void Execute()
    10.         {
    11.             // Add five million quads
    12.             for(int i = 0; i < 5000000; ++i )
    13.             {
    14.                 float3 p = origin + new float3(i %16, i / 16, 0);
    15.                 verts.Add(p + new float3(0, 0, 0));
    16.                 verts.Add(p + new float3(0, 1, 0));
    17.                 verts.Add(p + new float3(1, 1, 0));
    18.                 verts.Add(p + new float3(1, 0, 0));
    19.  
    20.                 indices.Add(i * 4 + 0);
    21.                 indices.Add(i * 4 + 1);
    22.                 indices.Add(i * 4 + 2);
    23.                 indices.Add(i * 4 + 0);
    24.                 indices.Add(i * 4 + 2);
    25.                 indices.Add(i * 4 + 3);
    26.  
    27.                 for (int j = 0; j < 4; ++j)
    28.                     colors.Add(Color.blue);
    29.             }
    30.         }
    31.     }
    Then assign them to the mesh whenever you want. I wanted to wait until my job is done then upload my changes:
    Code (CSharp):
    1.     void AddFaces(float3 pos)
    2.     {
    3.         var job = new AddFaceJob();
    4.         job.origin = pos;
    5.         job.verts = verts;
    6.         job.indices = indices;
    7.         job.colors = colors;
    8.         var handle = job.Schedule();
    9.  
    10.         StartCoroutine(UploadJobChanges(mesh, handle));
    11.     }
    12.  
    13.     IEnumerator UploadJobChanges(Mesh mesh, JobHandle job)
    14.     {
    15.         while (!job.IsCompleted)
    16.             yield return null;
    17.  
    18.         job.Complete();
    19.  
    20.         mesh.SetVertices<float3>(verts);
    21.         mesh.SetIndices<int>(indices, MeshTopology.Triangles, 0);
    22.      
    23.         mesh.RecalculateBounds();
    24.         mesh.RecalculateNormals();
    25.         mesh.RecalculateTangents();
    26.     }
    Note that even though I'm waiting for the job to complete inside the coroutine I still need to call job.Complete() afterwards, otherwise I get an error message about trying to access the nativearrays before the job is complete. I'm not sure if this is a bug or by design as some kind of safety measure. Either way it works great, my job runs in the background then updates the mesh once it completes.
     
    MNNoxMortem likes this.