Search Unity

Question MeshData GetVertexData returns weird value

Discussion in 'C# Job System' started by DevDunk, May 29, 2023.

  1. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    5,043
    I am trying to make a mesh slicer with the job system.
    I am looking into utelizing the meshdata API to fill a WritableMeshData with data.
    I am just highly confused why the array count of my GetVertexData is much higher than the list containing the count.
    My list has a length of 6 (capacity 32), but the array I get out of GetVertexData is 36. I am totally oblivious of the potential cause of this.

    Next to this I also get an error about the values in GetVertexData being ReadOnly, even when the mesh data is made with allocated writable mesh data.

    Any insight of this is welcome

    Code (CSharp):
    1. Debug.Log(LeftIndices.Length); //6
    2.             Debug.Log(LeftVertices.Length); //6
    3.             Debug.Log(LeftVertices.Capacity); //32
    4.             leftMesh.SetVertexBufferParams(LeftVertices.Length, vertexLayout);
    5.             leftMesh.SetIndexBufferParams(LeftIndices.Length, IndexFormat.UInt16);
    6.             var leftVerts = meshData.GetVertexData<BreakerVertex>();
    7.             var leftInds = meshData.GetIndexData<ushort>();
    8.             Debug.Log(leftInds.Length); //36
    9.             Debug.Log(leftVerts.Length); //36
    10. for (int i = 0; i < leftInds.Length; i++)
    11.             {
    12.                 leftInds[i] = LeftIndices[i]; //declared as [ReadOnly] but you are writing to it
    13.                 leftVerts[i] = LeftVertices[i];//declared as [ReadOnly] but you are writing to it
    14.             }
    15.  
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,890
    GetVertexData gives you what already exists. So I'd say you are expecting something different than what is there. What does the Mesh asset say if you select it in the Inspector?
    One common misconception would be the cube example: it has 8 vertices, right? Nope. It's actually 4 vertices times 6 sides, so 24 vertices overall since the vertices aren't shared with other sides.

    About the readonly: you are expected to use the data and merge it with your own, rather than replace it in-place. This is more efficient for Jobs usage because this can be optimized to one array being read-only and yours write-only (or readwrite), thus there won't be any multithreading collisions / locks and you can iterate massively parallel with IJobParallelFor.

    When you do use Jobs, I'd advise to keep the vertex and index jobs separate too. You should be able to run them in parallel. Even if that requires running a third setup/processing job it's usually a lot faster to have very simple, non-branching jobs over a single, complex job with multiple branches and using many fields/native collections.
     
  3. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    5,043
    How can there already be data on the mesh if the MeshData is made with Mesh.AllocateWritableMeshData. doesn't this create a new empty mesh (as in the documentation?)

    About the read-only, I removed all readonly and write only tags for now, since it's a sequential IJob for now, which did not remove the errors. The arrays/lists are actually made within the job, with only meshdata being the input and output
     
  4. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,890
  5. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    5,043
    Sure.
    I did notice the mesh I start out with does have 36 indices, but I don't touch the writable data until I set the values at the end, so that shouldn't happen, right?
    I thought I was getting a decent grasp on the meshdata API.

    For performance, I'll make the last for loop parallel for sure, doing the main loop is a lot harder, but that's another issue. Right now trying to get it working first before converting to parallel.

    Monobehavior side:

    Code (CSharp):
    1.     public void BreakAtPositionJob2(Vector3 position, Vector3 normal)
    2.     {    
    3.         var dataArray = Mesh.AcquireReadOnlyMeshData(mesh);
    4.         Mesh.MeshData data = dataArray[0];
    5.         Mesh.MeshDataArray leftMesh = Mesh.AllocateWritableMeshData(1);
    6.         Mesh.MeshDataArray rightMesh = Mesh.AllocateWritableMeshData(1);
    7.  
    8.         SliceMeshSequential3 sliceJob = new SliceMeshSequential3()
    9.         {
    10.             meshData = data,
    11.             leftMesh = leftMesh[0],
    12.             rightMesh = rightMesh[0],
    13.             Position = position,
    14.             Normal = normal
    15.         };
    16.         JobHandle sliceJobHandle = sliceJob.Schedule();
    17.         sliceJobHandle.Complete();
    18.  
    19.         Material mat = GetComponent<MeshRenderer>().material;
    20.  
    21.         GameObject go = new GameObject("SliceLeft");
    22.         Mesh newMesh = new Mesh();
    23.         Mesh.ApplyAndDisposeWritableMeshData(leftMesh, newMesh);
    24.         go.AddComponent<MeshFilter>().mesh = newMesh;
    25.         go.AddComponent<MeshRenderer>().material = mat;
    26.  
    27.         newMesh.UploadMeshData(false/* true or false depending on your use case */);
    28.         //Same for right mesh
    29.  
    30.         dataArray.Dispose();
    31.     }
    Job:

    Code (CSharp):
    1.  
    2. public struct SliceMeshSequential3 : IJob
    3.     {
    4.         public float3 Position, Normal;
    5.         public MeshData meshData;
    6.         public Mesh.MeshData leftMesh, rightMesh;
    7.  
    8.  
    9. public void Execute()
    10.         {
    11.             NativeArray<ushort> OriginalIndices = meshData.GetIndexData<ushort>();
    12.             NativeArray<OriginalVertex> OriginalVertices = meshData.GetVertexData<OriginalVertex>();
    13.  
    14.             int indexCount = OriginalIndices.Length;
    15.             NativeList<ushort> RightIndices = new NativeList<ushort>(indexCount/2, Allocator.Temp);
    16.             NativeList<ushort> LeftIndices = new NativeList<ushort>(indexCount/2, Allocator.Temp);
    17.             NativeList<BreakerVertex> RightVertices = new NativeList<BreakerVertex>(indexCount/2, Allocator.Temp);
    18.             NativeList<BreakerVertex> LeftVertices = new NativeList<BreakerVertex>(indexCount/2, Allocator.Temp);
    19.             Debug.Log(LeftIndices.Length);
    20.             Debug.Log(LeftVertices.Length);
    21.             short leftCounter = 0;
    22.             short rightCounter = 0;
    23.             for (int i = 0; i < OriginalIndices.Length; i += 3)
    24.             {
    25.                 int i1 = OriginalIndices[i];
    26.                 int i2 = OriginalIndices[i + 1];
    27.                 int i3 = OriginalIndices[i + 2];
    28.                 float3 v1 = OriginalVertices[OriginalIndices[i]].position;
    29.                 float3 v2 = OriginalVertices[OriginalIndices[i + 1]].position;
    30.                 float3 v3 = OriginalVertices[OriginalIndices[i + 2]].position;
    31.                 float2 uv1 = OriginalVertices[OriginalIndices[i]].uv;
    32.                 float2 uv2 = OriginalVertices[OriginalIndices[i + 1]].uv;
    33.                 float2 uv3 = OriginalVertices[OriginalIndices[i + 2]].uv;
    34.                 float3 n1 = OriginalVertices[OriginalIndices[i]].normal;
    35.                 float3 n2 = OriginalVertices[OriginalIndices[i + 1]].normal;
    36.                 float3 n3 = OriginalVertices[OriginalIndices[i + 2]].normal;
    37.  
    38.                 bool vert1Left = MeshTools.GetVertexSide(v1, Position, Normal) < 0;
    39.                 bool vert2Left = MeshTools.GetVertexSide(v2, Position, Normal) < 0;
    40.                 bool vert3Left = MeshTools.GetVertexSide(v3, Position, Normal) < 0;
    41.                 Debug.Log(vert1Left + "" + vert2Left + "" +vert3Left);
    42.                 Debug.Log(v1 + "" + v2 + "" + v3);
    43.  
    44.  
    45.                 if (vert1Left && vert2Left && vert3Left) //Verts to Left
    46.                 {
    47.                     Debug.Log("adding left");
    48.                     leftCounter += 3;
    49.                     LeftVertices.Add(new BreakerVertex()
    50.                     {
    51.                         position = v1,
    52.                         normal = n1,
    53.                         uv = uv1
    54.                     });
    55.                     LeftVertices.Add(new BreakerVertex()
    56.                     {
    57.                         position = v2,
    58.                         normal = n2,
    59.                         uv = uv2
    60.                     });
    61.                     LeftVertices.Add(new BreakerVertex()
    62.                     {
    63.                         position = v3,
    64.                         normal = n3,
    65.                         uv = uv3
    66.                     });
    67.                     LeftIndices.Add((ushort)(leftCounter - 3));
    68.                     LeftIndices.Add((ushort)(leftCounter - 2));
    69.                     LeftIndices.Add((ushort)(leftCounter - 1));
    70.                 }
    71.                 else if (!vert1Left && !vert2Left && !vert3Left) //Verts to Right
    72.                 {
    73.                     Debug.Log("adding right");
    74.                     rightCounter += 3;
    75.                     RightVertices.Add(new BreakerVertex()
    76.                     {
    77.                         position = v1,
    78.                         normal = n1,
    79.                         uv = uv1
    80.                     });
    81.                     RightVertices.Add(new BreakerVertex()
    82.                     {
    83.                         position = v2,
    84.                         normal = n2,
    85.                         uv = uv2
    86.                     });
    87.                     RightVertices.Add(new BreakerVertex()
    88.                     {
    89.                         position = v3,
    90.                         normal = n3,
    91.                         uv = uv3
    92.                     });
    93.                     RightIndices.Add((ushort)(rightCounter - 3));
    94.                     RightIndices.Add((ushort)(rightCounter - 2));
    95.                     RightIndices.Add((ushort)(rightCounter - 1));
    96.                 }
    97.                 else //Vertes split, skipped for testing purposes
    98.                 {
    99.                     continue;
    100.                 }
    101.             }
    102.  
    103.             VertexAttributeDescriptor[] vertexLayout =  {
    104.                 new VertexAttributeDescriptor { attribute = VertexAttribute.Position, format = VertexAttributeFormat.Float32, dimension = 3 },
    105.                 new VertexAttributeDescriptor { attribute = VertexAttribute.Normal, format = VertexAttributeFormat.Float32, dimension = 3 },
    106.                 new VertexAttributeDescriptor { attribute = VertexAttribute.TexCoord0, format = VertexAttributeFormat.Float32, dimension = 2 } };
    107.  
    108.             Debug.Log(LeftIndices.Length); //6
    109.             Debug.Log(LeftVertices.Length); //6
    110.             Debug.Log(LeftVertices.Capacity); //32
    111.             leftMesh.SetVertexBufferParams(LeftVertices.Length, vertexLayout);
    112.             leftMesh.SetIndexBufferParams(LeftIndices.Length, IndexFormat.UInt16);
    113.             var leftVerts = meshData.GetVertexData<BreakerVertex>();
    114.             var leftInds = meshData.GetIndexData<ushort>();
    115.             Debug.Log(leftInds.Length); //36
    116.             Debug.Log(leftVerts.Length); //36
    117.             for (int i = 0; i < leftInds.Length; i++)
    118.             {
    119.                 leftVerts[i] = LeftVertices[i];
    120.                 leftInds[i] = LeftIndices[i];
    121.             }
    122.             Debug.Log(RightIndices.Length);
    123.             Debug.Log(RightVertices.Length);
    124.             rightMesh.SetVertexBufferParams(RightVertices.Length, vertexLayout);
    125.             rightMesh.SetIndexBufferParams(RightIndices.Length, IndexFormat.UInt16);
    126.             var rightVerts = meshData.GetVertexData<BreakerVertex>();
    127.             var rightInds = meshData.GetIndexData<ushort>();
    128.  
    129.             for (int i = 0; i < rightInds.Length; i++)
    130.             {
    131.                 rightInds[i] = RightIndices[i];
    132.                 rightVerts[i] = RightVertices[i];
    133.             }
    134.         }
    135. }
     
  6. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,890
    36 indices would actually indicate (no pun) that you're starting out with a cube: two triangles per side equals 6 indices times 6 sides equals 36 indices.

    You could simplify the code somewhat by replacing the numbered v/uv/n with just what they are: an instacne of BreakerVertex. Or more generally speaking: a vertex struct that contains position, normal and uv.