Using ECS to update a mesh.

Yes I’m using traditional mesh renderers as I found that they are more than twice as fast for unique meshes when you can’t benefit from instancing.

Using MeshRenderers (415 ‘fps’, 2.4ms cpu, 0.7ms render)

Using MeshInstanceRenderSystem (187 ‘fps’, 5.4ms cpu, 1.9ms render)

From my original post here: here: MeshInstanceRenderSystem (poor) performance

As for my backface culling, I just do a very simple position check that disables faces. It doesn’t do any tunnel checks or anything, I don’t need it as this project is mostly limited to 2D from a top down perspective that just uses a voxel type landscape. You could totally do some type of flood fill like minecraft does though if you wanted.

        [BurstCompile]
        private struct CullFacesJob : IJobParallelFor
        {
            public float3 CullFrom;
            public int3 ChunkSize;

            [ReadOnly] public EntityArray Entities;
            [ReadOnly] public ComponentDataArray<GridPosition> Positions;
            public ComponentDataArray<FaceCull> Culling;

            public NativeQueue<Entity>.Concurrent EntitiesToCull;
         
            public void Execute(int index)
            {
                var facesToCull = new FaceCull();
                var position = Positions[index].Value;

                if (CullFrom.x < position.x - ChunkSize.x) facesToCull.Right = true;
                if (CullFrom.x > position.x + ChunkSize.x + ChunkSize.x) facesToCull.Left = true;
                if (CullFrom.y < position.y - ChunkSize.y) facesToCull.Top = true;
                if (CullFrom.y > position.y + ChunkSize.y + ChunkSize.y) facesToCull.Bottom = true;
                if (CullFrom.z < position.z - ChunkSize.z) facesToCull.Back = true;
                if (CullFrom.z > position.z + ChunkSize.z + ChunkSize.z) facesToCull.Front = true;

                if (!facesToCull.Equals(Culling[index]))
                {
                    Culling[index] = facesToCull;
                    EntitiesToCull.Enqueue(Entities[index]);
                }
            }
        }

I generate each side of my mesh separately so I can easily put it back together every time I have to change which face is culled. The only real cost is calculating the indices offsets.

        private void UpdateChunk(ChunkMesh chunk, FaceCull culling)
        {        
            for (var index = 0; index < 6; index++)
            {
                if (culling.IsCulled(index))
                    continue;
             
                var face = chunk.MeshFaces.GetFace(index);
                face.Resize();

                // Need to offset our indices
                var current = _indices.Count;
                var indices = face.Indices.List;
                _indices.AddRange(indices);

                var indicesCount = _indices.Count;
                var offset = _vertices.Count;

                // we could UnsafeNativeList increment this in a job for better performance?
                for (var i = current; i < indicesCount; i++)
                    _indices[i] += offset;
             
                _vertices.AddRange(face.Vertices.List);
                _normals.AddRange(face.Normals.List);
                _uvs.AddRange(face.UVs.List);
            }

            var mesh = chunk.Presenter.Mesh;
            mesh.Clear();

            if (_vertices.Count == 0)
                return;
         
            mesh.SetVertices(_vertices);
            mesh.SetNormals(_normals);
            mesh.SetUVs(0, _uvs);
            mesh.SetTriangles(_indices, 0);
         
            _vertices.Clear();
            _normals.Clear();
            _uvs.Clear();
            _indices.Clear();
        }
2 Likes