Reputation: 1249
For a small set of custom terrain tools I'm making to be used at runtime in Unity I need to be able to "paint" areas of the terrain to raise and lower it.
To do that I have this function:
public void PaintRaise(Vector3 center, float radius, float power) {
Mesh mesh = this.gameObject.GetComponent<MeshFilter>().sharedMesh;
Vector3[] verts = new Vector3[mesh.vertices.Length];
for (int i = 0; i < mesh.vertices.Length; ++i) {
// method for getting distance, faster then Vector3.Distance
var heading = mesh.vertices[i] - center;
var distance = heading.magnitude;
var direction = heading / distance;
if (heading.sqrMagnitude < radius * radius) {
verts[i] = new Vector3(
mesh.vertices[i].x,
mesh.vertices[i].y + power,
mesh.vertices[i].z);
} else {
verts[i] = mesh.vertices[i];
}
}
mesh.vertices = verts;
}
In theory, it should work perfectly applied on a GameObject with a mesh, and it does! But unfortunately it is very, very slow. It takes around 10 seconds to process for a radius of 5 units, even with this improved distance finding method.
It runs just as slow in both the editor and in a compiled build.
As you can see the profiler shows the Update() function that calls the PaintRaise function taking ~13 seconds to execute a single frame, most of it being the garabage collector!
Why is it running slow and how can I speed it up (so it ideally takes a few milliseconds to execute)?
EDIT
Enabling deep profiling makes it even more confusing! Why is getting the mesh vertices taking 13 seconds due to the GC?
Upvotes: 1
Views: 4800
Reputation: 1249
After doing some research I found out the vertices is not actually a variable like it looks like.
There are several reasons for the new SetVertices / SetIndices / SetUVs / methods. First of all the old vertices property is not a variable. This fact has confused many users. It's a property. So actually there's a get method and a set method but the usage just looks like a variable.
What most did not understand is that the getter does not return a reference to an internal array but returns a copy. This is necessary since the actual vertex data is stored in the native C++ side of Unity.
Changing my function to use Get() and Set() eliminated the extra garbage collection created by using the vertices property and made it run buttery smooth.
public void PaintRaise(Vector3 center, float radius, float power) {
Vector3 localPoint = transform.InverseTransformPoint(center);
Mesh mesh = this.gameObject.GetComponent<MeshFilter>().sharedMesh;
List<Vector3> verts = new List<Vector3>();
mesh.GetVertices(verts);
for (int i = 0; i < verts.Count; ++i) {
var heading = verts[i] - center;
var distance = heading.magnitude;
var direction = heading / distance;
if (heading.sqrMagnitude < radius * radius) {
verts[i] = new Vector3(
verts[i].x,
verts[i].y + power,
verts[i].z);
}
}
mesh.SetVertices(verts);
}
Upvotes: 1