Mesh Splitting

Posted by Adam on 16 July 2018 at 1:33 pm

While Unity may have introduced 32-bit Mesh index buffers, by default it still uses 16-bit indices, which means a regular mesh object is limited to 65,535 vertices. This is a high number, and arguably, you may ask if any object should really reach this limit. For the ground layers in our medieval settlement simulator, reaching this limit has been fairly easy.

With regions consisting of, by default, 200x200 tiles, or 40,000 separate tiles, and with each tile consisting of 4 vertices, we have 160,000 vertices for the ground terrain alone. These are split among the different map layers: soil, rich soil, sand, clay, etc, so on a diverse map, it's easy to have ~20,000 vertices per mesh, but then, on very homogeneous terrain, you can easily breach the 65,535 vertex limit. The result is a truncated mesh.

Truncated mesh

This isn't great. It's the opposite of great. It's terrible.

To combat this, we need to detect when our procedurally generated terrain layers have exceeded this limit, and create multiple mesh objects. When we're ready to generate the meshes (which only happens in the very beginning, and later, only if a change has been made to the terrain) we iterate through every vertex in the mesh, and when we hit the limit, we create a new mesh and carry on counting.

Importantly, we make sure that for each "mesh split", we assign them to their parents, which in our case is each respective map layer.

// Create a new GameObject and attach it to the parent container.
GameObject gameObject = new GameObject(string.Format("MeshSplit_{0}", labelSuffix));
gameObject.transform.parent = _parent.transform;
gameObject.transform.position = _parent.transform.position;

We then create a new MeshRenderer component and assign a material, which uses a basic unlit shader with vertex blending (which we wrote about in our previous article on Edge Smoothing).

// Create a new MeshRenderer and assign the material.
MeshRenderer meshRenderer = gameObject.AddComponent<MeshRenderer>();
meshRenderer.material = _material;

Finally, we create a MeshFilter component and assign the relevant data to the mesh property.

// Create a new MeshFilter and set the data.
MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>();
meshFilter.mesh.vertices = vertices.ToArray();
meshFilter.mesh.uv = uvs.ToArray();
meshFilter.mesh.colors = colors.ToArray();
meshFilter.mesh.triangles = triangles.ToArray();

With meshes now limited to 65,535 vertices, you can see that our rather large Soil layer is now split into 3 layers.

Split mesh

I'm sure there are lots of improvements that can be made. Later on we'll be looking at finding large cuboid segments that can be simplified into a single quad, but for now, there is so much more to do. But terrain blending is looking really nice. (Yes, I've been working on the textures.)

Terrain blending

Enjoyed this quick write up? Why not follow us on Twitter or Patreon?

This post is from the First Era of Plump Helmet.

This was before the great break of 2019-2021 and the predominant project of the time was Feudalia. The project was placed into cold storage and has not been revived yet. The dream remains alive however. These posts have been kept for posterity.