- Home /
Delete vertices from a mesh at runtime
Hi all,
I've been attempting to create a C# script that removes vertices above a certain point in world space. So far I have managed to create a script that makes the offending part of the mesh transparent but I'm having alpha issues (particularly with the DOF effect.) I also thought it would be more efficient if the vertices didnt exist at all. I'm VERY new to programming so I apologise in advance for the upcoming code!
using UnityEngine; using System.Collections; using System.Collections.Generic;
public class DeleteVertsByHeight : MonoBehaviour {
public GameObject heightReferenceObject;
public float heightReferenceFloat = 0;
float heightCutOff;
public float errorAdjustment=0;
void Start() {
if (heightReferenceObject != null) {
heightCutOff = heightReferenceObject.transform.position.y;
}
else{
heightCutOff = heightReferenceFloat;
}
Mesh mesh = GetComponent<MeshFilter>().mesh;
int[] triangles = mesh.triangles;
Vector3[] vertices = mesh.vertices;
Vector2[] uv = mesh.uv;
Vector3[] normals = mesh.normals;
List<Vector3> vertList = new List<Vector3>();
List<Vector2> uvList = new List<Vector2>();
List<Vector3> normalsList = new List<Vector3>();
List<int> trianglesList = new List<int>();
int i = 0;
while (i < vertices.Length) {
Vector3 tempPos = transform.TransformPoint(vertices[i].x, vertices[i].y, vertices[i].z);
if(tempPos.y < (heightCutOff+errorAdjustment))
{
vertList.Add (vertices[i]);
uvList.Add (uv[i]);
normalsList.Add (normals[i]);
trianglesList.Add (triangles[i]); //Don't think this is generating them in the correct order!
}
i++;
}
triangles = trianglesList.ToArray ();
vertices = vertList.ToArray ();
uv = uvList.ToArray ();
normals = normalsList.ToArray ();
mesh.triangles = triangles;
mesh.vertices = vertices;
mesh.uv = uv;
mesh.normals = normals;
}
}
Answer by robertbu · Jul 02, 2014 at 04:30 PM
This can be a tricky problem. I'm not sure what you want to do about the edges. That is, a triangle may start below your height, and then extend above it. Deleting the triangle may result in a ragged edge. If you don't care (or if it a non-issue because of the nature of your mesh), then as a first try, consider leaving the vertices alone and just eliminating any triangles that have any vertex above your 'heightCuttoff'. The Triangles array is composed of sets of three indices (into the vertices array). So you can just walk the array checking each set of three. If all the vertices are below your cutoff, add all three to a new array. If any are above, skip that set of three. This may get you want you want without deleting any vertices.
If you really want to eliminate the vertices, then you could do it in two steps:
Build a new vertices list with vertices eliminated, but create a map from the old array position to the new array position. That is you would have an array of integers the size of the old vertices list. When a vertex is put into the new array, that position is stored in the integer array at the old vertex array position. If a vertex is eliminated, set the map value for that position to -1.
Walk the triangle list building a new array of triangles. If any of the three vertices when indexed into the map array crated in step #1 are -1, don't move that triangle to the new list. Else, adjust the indices using the map and add the triangle to build the new list.
Deleting the triangle may result in a ragged edge. If you don't care (or if it a non-issue because of the nature of your mesh),
This should be O$$anonymous$$ with the mesh I am using, or I could just use a combination of my existing solution and change the alpha per vert to cover the ragged edge.
The Triangles array is composed of sets of three indices (into the vertices array).
I'm not sure I follow this. I noticed that the triangle array is an int array. Does this mean that the first 3 entries in the vert array make up triangle #1, the next 3 make up triangle #2 etc etc?
Edit: Just realised that triangles will share verts so this won't be the case :P
The triangle array are indices into the vertices array. The first three entries in the array form the first triangle, and the second three entries, the second triangle. For example the first six entries in the array might be:
0,1,2,0,1,4
These would be indexes into the vertices array. The first triangle would use the first three Vector3 positions in the vertices array. The second triangle would share the first two positions, but have a different third position.
Here is a bit of untested sample code showing the logic:
for (int i = 0; i < triangles.Length; i += 3) {
if ((transform.TransformPoint(vertices[triangles[i ]]).y < heightCutOff+errorAdjustment) &&
(transform.TransformPoint(vertices[triangles[i+1]]).y < heightCutOff+errorAdjustment) &&
(transform.TransformPoint(vertices[triangles[i+2]]).y < heightCutOff+errorAdjustment)) {
newTriangles.Add (triangles[i]);
newTriangles.Add (triangles[i+1]);
newTriangles.Add (triangles[i+2]);
}
}
Does this mean that the first 3 entries in the vert array make up triangle #1, the next 3 make up triangle #2 etc etc?
@$$anonymous$$affew - no. The first three entries in the triangle array make up the first triangle. The second three make up the second triangle. The integers in the triangle array indicate what entries in the vertices array to use for each point of the triangle. By using integers as indices into the vertices array, many triangles can share the same vertex.
Note given your statements above about using vertex color and setting the alpha to 0, one thing you could do is to eli$$anonymous$$ate all the triangles where all three vertices are above your heightCutOff, and then use an alpha of 0 for any remaining points on triangles are above heightCutOff.
Answer by Maffew · Jul 03, 2014 at 09:23 AM
@robertu that sample code helped massively, thanks!
here is my working code for anyone interested.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class DeleteVertsByHeight : MonoBehaviour {
public GameObject heightReferenceObject;
public float heightReferenceFloat = 0;
float heightCutOff;
public float errorAdjustment=0;
void Start() {
Mesh mesh = GetComponent<MeshFilter>().mesh;
int[] triangles = mesh.triangles;
Vector3[] vertices = mesh.vertices;
Vector2[] uv = mesh.uv;
Vector3[] normals = mesh.normals;
List<Vector3> vertList = new List<Vector3>();
List<Vector2> uvList = new List<Vector2>();
List<Vector3> normalsList = new List<Vector3>();
List<int> trianglesList = new List<int>();
if (heightReferenceObject != null) {
heightCutOff = heightReferenceObject.transform.position.y;
}
else{
heightCutOff = heightReferenceFloat;
}
int i = 0;
while (i < vertices.Length) {
vertList.Add (vertices[i]);
uvList.Add (uv[i]);
normalsList.Add (normals[i]);
i++;
}
for (int triCount = 0; triCount < triangles.Length; triCount += 3) {
if ((transform.TransformPoint(vertices[triangles[triCount ]]).y < heightCutOff+errorAdjustment) &&
(transform.TransformPoint(vertices[triangles[triCount+1]]).y < heightCutOff+errorAdjustment) &&
(transform.TransformPoint(vertices[triangles[triCount+2]]).y < heightCutOff+errorAdjustment)) {
trianglesList.Add (triangles[triCount]);
trianglesList.Add (triangles[triCount+1]);
trianglesList.Add (triangles[triCount+2]);
}
}
triangles = trianglesList.ToArray ();
vertices = vertList.ToArray ();
uv = uvList.ToArray ();
normals = normalsList.ToArray ();
//mesh.Clear();
mesh.triangles = triangles;
mesh.vertices = vertices;
mesh.uv = uv;
mesh.normals = normals;
}
}
List function ToArray takes huge GC alloc if list is long. Is there better way in the terms of GC allocation size?
Answer by radacadabra · May 08, 2020 at 12:46 AM
This is an old thread, but if you stumble upont his like I did, here is a better optimised code that does the same.
@Maffew code works (thanks btw!) but checks vertices for each triangle, so one vertex is checked multiple times for each triangle it makes. On smaller meshes it doesn't matter but for larger ones it adds up. Code below first checks all vertices once and marks them inside or not.
Also, there is no need (as far as I know) to rewrite vertices, uv, and normals - you can only adjust the triangles. This way, vertices, uv and normals that are not used by a triangle are just not used. Result is the same but but cutting is faster.
Code below cuts not only above certain y value, but above max and min y, x and z - basically you can cut a box from your mesh. It can of course be easily adjusted according to need.
//set cut-off values
float maxX;
float minX;
float maxY;
float minY;
float maxZ;
float minZ;
//set the buffer(padding)
float buffer;
maxX = maxX + buffer;
minX = minX - buffer;
maxY = maxY + buffer;
minY = minY - buffer;
maxZ = maxZ + buffer;
minZ = minZ - buffer;
Mesh mesh = meshClone.GetComponent<MeshFilter>().mesh;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
List<int> newTriangles = new List<int>();
float vertexX;
float vertexY;
float vertexZ;
for (int i = 0; i < vertices.Length; i++)
{
vertexX = transform.TransformPoint(vertices[i]).x;
vertexY = transform.TransformPoint(vertices[i]).y;
vertexZ = transform.TransformPoint(vertices[i]).z;
if (vertexX < maxX && vertexX > minX && vertexY < maxY && vertexY > minY && vertexZ < maxZ && vertexZ > minZ)
{
insideOrNot.Add(true);
}
else
{
insideOrNot.Add(false);
}
}
for (int i = 0; i < triangles.Length; i += 3)
{
if (insideOrNot[triangles[i]] && insideOrNot[triangles[i + 1]] && insideOrNot[triangles[i + 2]])
{
newTriangles.Add(triangles[i]);
newTriangles.Add(triangles[i + 1]);
newTriangles.Add(triangles[i + 2]);
}
}
mesh.triangles = newTriangles.ToArray();
Thanks for posting this! I'm intrigued but confused. What is 'insideOrNot'?
private List<bool> insideOrNot = new List<bool>();
public Transform meshClone;
//set cut-off values
public float maxX;
public float $$anonymous$$X;
public float maxY;
public float $$anonymous$$Y;
public float maxZ;
public float $$anonymous$$Z;
//set the buffer(padding)
private float buffer;
should hold you
Your answer
