
Recently, on one of the groups, someone asked how to cut shapes in the mesh. It got me thinking about how you can actually do that?
After some digging, I found out that there could be more than one solution to that. So why don’t we try to see which one would work the best?
Let’s start today by generating a mesh in which we will cut some shapes!
The idea ?
In this post, we will create a mesh based on a 2-dimensional array of bools. Each element in the array will say if we should put a mesh at that position or just skip it. This will be much easier than trying to create a solution for everything.
The other element of our puzzle will be using a pointer to draw shapes in the mesh. We will also set the size of our “brush”, so we can make smaller or bigger changes.
Coding ??
The first thing that we want to do is to generate a mesh. We already talked about how to do that in posts about generating a quad and a plane for the terrain.
To do that we need to know how big our mesh has to be and how small pieces are going to be. We will also need to have a 2-dimensional array which will tell where we should and where we shouldn’t put our mesh.
Let’s lay down our foundation.
using UnityEngine; /// <summary> /// This class controls the mesh and removal of its parts. /// </summary> [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] public class MeshController : MonoBehaviour { // Width of the mesh. public int width = 10; // Height of the mesh. public int height = 20; // Size of each quad in the mesh. public float tileSize = 0.8f; // 2-dimensional array that the mesh is based on. private bool[,] wallGrid; // Reference to MeshFilter. private MeshFilter meshFilter; // Position where grid begins. // Created for convenience. private Vector3 gridBegin; /// <summary> /// Unity method called on object creation. /// </summary> private void Awake() { meshFilter = GetComponent<MeshFilter>(); // Calculating where grid should begin. gridBegin = new Vector3((-width / 2f + .5f) * tileSize, (-height / 2f + .5f) * tileSize, 0); // Creting 2-dimensional array for the mesh. wallGrid = new bool[width, height]; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { wallGrid[x, y] = true; } } } /// <summary> /// Unity method called on the first frame. /// </summary> void Start() { GenerateMesh(); } /// <summary> /// Method responsible for generating mesh based on 2-dimensional array. /// </summary> private void GenerateMesh() { } }
Problems ?
Now we could use lists for our convenience. Unfortunately, we would have huge memory allocation each time we would regenerate our mesh.

So to optimize our code a little bit, we are going to use arrays.

To create our mesh, we need to know how many vertices and triangles we need. We can calculate that by counting places where we need to put a mesh. For each place, we need four values for vertices and six values for triangles to create a quad.
using UnityEngine; /// <summary> /// This class controls the mesh and removal of its parts. /// </summary> [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] public class MeshController : MonoBehaviour { ... /// <summary> /// Method responsible for generating mesh based on 2-dimensional array. /// </summary> private void GenerateMesh() { // Getting mesh instance. Mesh mesh = meshFilter.mesh; if (mesh == null) { mesh = new Mesh(); } mesh.Clear(); // Counting how many quads needs to be created. int w = 0; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (wallGrid[x, y]) { w++; } } } // Preparing variables for creating a mesh. var vertices = new Vector3[w * 4]; var triangles = new int[w * 6]; int vertIndex = 0; int triIndex = 0; } }
The next step is to go through our 2-dimensional array and generate vertices and triangles for the mesh. We can also reduce a few calculations for the vertices, as each of them will have the same offset from the place center.
using UnityEngine; /// <summary> /// This class controls the mesh and removal of its parts. /// </summary> [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] public class MeshController : MonoBehaviour { ... /// <summary> /// Method responsible for generating mesh based on 2-dimensional array. /// </summary> private void GenerateMesh() { ... // Caching few calculations for the quad. var bottomLeft = new Vector3(-tileSize / 2, -tileSize / 2, 0); var topLeft = new Vector3(-tileSize / 2, tileSize / 2, 0); var topRight = new Vector3(tileSize / 2, tileSize / 2, 0); var bottomRight = new Vector3(tileSize / 2, -tileSize / 2, 0); // Looping over 2-dimensional array to generate quads. for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (wallGrid[x, y]) { // Calculating center of the quad. var center = gridBegin + new Vector3(x, y, 0) * tileSize; // Assigning vertices vertices[vertIndex + 0] = (center + bottomLeft); vertices[vertIndex + 1] = (center + topLeft); vertices[vertIndex + 2] = (center + topRight); vertices[vertIndex + 3] = (center + bottomRight); // Assigning triangles triangles[triIndex + 0] = (vertIndex + 0); triangles[triIndex + 1] = (vertIndex + 1); triangles[triIndex + 2] = (vertIndex + 2); triangles[triIndex + 3] = (vertIndex + 0); triangles[triIndex + 4] = (vertIndex + 2); triangles[triIndex + 5] = (vertIndex + 3); // Increasing indexes for vertices and triangles. vertIndex += 4; triIndex += 6; } } } } }
Great! Now we need to assign these arrays to the mesh and MeshFilter.
using UnityEngine; /// <summary> /// This class controls the mesh and removal of its parts. /// </summary> [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] public class MeshController : MonoBehaviour { ... /// <summary> /// Method responsible for generating mesh based on 2-dimensional array. /// </summary> private void GenerateMesh() { ... // Assigning arrays to the mesh. mesh.vertices = vertices; mesh.triangles = triangles; // Updating mesh. meshFilter.mesh = mesh; // Cleaning arrays. vertices = null; triangles = null; } }
Now we have our mesh again! ?
Next on our list is to draw in it! Let’s start by making a simple class to gather input events from the screen.
using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; /// <summary> /// Input layer reads pointer actions on the screen. /// </summary> public class InputLayer : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerDownHandler { /// <summary> /// Action called when user press pointer. /// </summary> public UnityAction<Vector2> PointerDown; /// <summary> /// Unity OnPointerDown event. /// </summary> /// <param name="eventData">Event data.</param> public void OnPointerDown(PointerEventData eventData) { PointerDown?.Invoke(eventData.position); } /// <summary> /// Action called when user begin drag. /// </summary> public UnityAction<Vector2> DragBegin; /// <summary> /// Unity OnBeginDrag event. /// </summary> /// <param name="eventData">Event data.</param> public void OnBeginDrag(PointerEventData eventData) { DragBegin?.Invoke(eventData.position); } /// <summary> /// Action called when user continue drag. /// </summary> public UnityAction<Vector2> Drag; /// <summary> /// Unity OnDrag event. /// </summary> /// <param name="eventData">Event data.</param> public void OnDrag(PointerEventData eventData) { Drag?.Invoke(eventData.position); } /// <summary> /// Action called when user end drag. /// </summary> public UnityAction<Vector2> DragEnd; /// <summary> ///Unity OnEndDrag event. /// </summary> /// <param name="eventData">Event data.</param> public void OnEndDrag(PointerEventData eventData) { DragEnd?.Invoke(eventData.position); } }
Now we will need something that will use input and tell our mesh to remove its parts.
using UnityEngine; /// <summary> /// Removal Pointer. /// Calculates world position of user input and passes it to the mesh. /// </summary> public class RemovalPointer : MonoBehaviour { // Reference to the input layer to attach actions. public InputLayer inputLayer; // Reference to the mesh controller. public MeshController mesh; // Reference to the main camera. public Camera main; // Removal radius. public float hitRadius = 2; /// <summary> /// Unity method called when component is enabled. /// </summary> private void OnEnable() { inputLayer.PointerDown += OnPointerDown; inputLayer.DragBegin += OnDragBegin; inputLayer.Drag += OnDrag; inputLayer.DragEnd += OnDragEnd; } /// <summary> /// Unity method called when component is disabled. /// </summary> private void OnDisable() { inputLayer.PointerDown -= OnPointerDown; inputLayer.DragBegin -= OnDragBegin; inputLayer.Drag -= OnDrag; inputLayer.DragEnd -= OnDragEnd; } /// <summary> /// Action on begin drag. /// </summary> /// <param name="screenPosition">Screen position.</param> private void OnDragBegin(Vector2 screenPosition) { RemovePartsOfMeshAt(screenPosition); } /// <summary> /// Action on drag. /// </summary> /// <param name="screenPosition">Screen position.</param> private void OnDrag(Vector2 screenPosition) { RemovePartsOfMeshAt(screenPosition); } /// <summary> /// Action on end drag. /// </summary> /// <param name="screenPosition">Screen position.</param> private void OnDragEnd(Vector2 screenPosition) { RemovePartsOfMeshAt(screenPosition); } /// <summary> /// Action on pointer down. /// </summary> /// <param name="screenPosition">Screen position.</param> private void OnPointerDown(Vector2 screenPosition) { RemovePartsOfMeshAt(screenPosition); } /// <summary> /// Removes parts of the mesh at Screen Position. /// </summary> /// <param name="screenPosition">Screen position.</param> private void RemovePartsOfMeshAt(Vector2 screenPosition) { } }
Problems ?
Originally I tried to use Physics with Raycast and MeshCollider to make it work. It worked, but only when the mesh wasn’t too big, so I decided to ditch it and calculate which elements to remove without using physics.
Before we finish our RemovalPointer, we need to add remove method to our MeshController. This method will take two parameters – position in the world and the radius at which we want to remove parts of the mesh.
using UnityEngine; /// <summary> /// This class controls the mesh and removal of its parts. /// </summary> [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] public class MeshController : MonoBehaviour { ... /// <summary> /// Removes parts of the mesh at provided positions and range. /// </summary> /// <param name="position">Position in world.</param> /// <param name="range">Range of removing.</param> public void RemoveMeshAt(Vector2 position, float range) { // Converting world position to the indexes in 2-dimensional array. var positionIndex = ((position + Vector2.one * tileSize * .5f) - new Vector2(gridBegin.x, gridBegin.y)) / tileSize; // Converting range into index range. var indexRange = range / tileSize; // Calculating square in which elements are being removed // Width int startX = Mathf.CeilToInt(Mathf.Max(0, positionIndex.x - indexRange)); int endX = Mathf.FloorToInt(Mathf.Min(width, positionIndex.x + indexRange)); // Height int startY = Mathf.CeilToInt(Mathf.Max(0, positionIndex.y - indexRange)); int endY = Mathf.FloorToInt(Mathf.Min(height, positionIndex.y + indexRange)); // Flag if we need to update a mesh. bool needUpdateMesh = false; // Looping through array to remove elements in range. for (int x = startX; x < endX; x++) { for (int y = startY; y < endY; y++) { // Check if element is in range of removal if (Vector2.Distance(positionIndex, new Vector2(x, y)) > indexRange) { continue; } // If part of mesh exists and it is being removed, we have to update a mesh. if (wallGrid[x, y]) { needUpdateMesh = true; } wallGrid[x, y] = false; } } // If update in mesh is needed, run generate mesh again. if (needUpdateMesh) { GenerateMesh(); } } }
This method also checks if mesh needs to be regenerated not to do it for no reason.
Now let’s finish our RemovalPointer!
using UnityEngine; /// <summary> /// Removal Pointer. /// Calculates world position of user input and passes it to the mesh. /// </summary> public class RemovalPointer : MonoBehaviour { ... /// <summary> /// Removes parts of the mesh at Screen Position. /// </summary> /// <param name="screenPosition">Screen position.</param> private void RemovePartsOfMeshAt(Vector2 screenPosition) { // Calculating world position. var worldPoint = main.ScreenToWorldPoint(screenPosition); var hitPoint = new Vector2(worldPoint.x, worldPoint.y); // Passing data to the mesh to remove its parts. mesh.RemoveMeshAt(hitPoint, hitRadius); } }
Awesome! It looks like we have everything ready! Let’s see how it works!
The result ?

I think it looks awesome! But it also needs some further work if you plan to use this with some big meshes. Remember, meshes in Unity could only have up to 64k vertices!
What do you think about it? Will it be useful for you? Let me know in the comment section below!
If you want to get notified on future content, sign up for the newsletter!
As always, the whole project is available at my public repository. ?
And I hope to see you next time! ?