- Home /
Dynamically move vertices of a mesh
Hello! I just start learning to make procedural mesh from code. I made a procedural mesh ( just a simple triangle ) and want to randomly change the vertices' position when pressing a key. Here is my code:
using UnityEngine;
using System.Collections;
public class MyMesh : MonoBehaviour {
public Vector3 p1 = new Vector3(-1,0,0);
public Vector3 p2 = new Vector3(0,2,0);
public Vector3 p3 = new Vector3(1,0,0);
void Start () {
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mesh = mf.mesh;
Vector3[] vertices = new Vector3[]
{
p1,
p2,
p3
};
int[] triangles = new int[]
{
0,1,2
};
Vector3[] normals = new Vector3[]
{
Vector3.forward,
Vector3.forward,
Vector3.forward
};
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.normals = normals;
}
void Update () {
if(Input.GetKeyDown("up")){
p1 = Random.insideUnitSphere;
p2 = Random.insideUnitSphere;
p3 = Random.insideUnitSphere;
Debug.Log("Up is pressed.");
}
}
}
When I hit play, the mesh showed up correctly. And then I pressed the up button, I saw the console show "Up is pressed." and the position of p1, p2, p3 change in the inspector. But the mesh in the game view looks the same, didn't change its shape. Where did I get wrong?
Also tried putting all of the code into Update().
using UnityEngine;
using System.Collections;
public class MyMesh1 : MonoBehaviour {
public Vector3 p1 = new Vector3(-1,0,0);
public Vector3 p2 = new Vector3(0,2,0);
public Vector3 p3 = new Vector3(1,0,0);
void Update () {
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mesh = mf.mesh;
Vector3[] vertices = new Vector3[]
{
p1,
p2,
p3
};
int[] triangles = new int[]
{
0,1,2
};
Vector3[] normals = new Vector3[]
{
Vector3.forward,
Vector3.forward,
Vector3.forward
};
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.normals = normals;
if(Input.GetKeyDown("up")){
p1 = Random.insideUnitSphere;
p2 = Random.insideUnitSphere;
p3 = Random.insideUnitSphere;
Debug.Log("Up is pressed.");
}
}
}
It works just like what I want: Every time I press the up button, the mesh's vertices change their position so the mesh changes its shape randomly. However, Update() is called every frame so I am worried that it's really bad to put GetComponent, new Vector3 array etc. in Update()! Are there any other good ways to dynamically deform a mesh? Thank you!!
You can use $$anonymous$$esh.$$anonymous$$arkDynamic to move around a lot of vertices every frame with a mesh. You can do it for quite a lot of vertices before you get a significant performance impact.
Answer by NoseKills · Jan 19, 2016 at 06:49 PM
There are a few issues in your approach. Firstly, totally unrelated to meshes: When you do something like this (essentially what you are doing now)
public Vector3 p1 = new Vector3(-1,0,0);
Vector3[] vertices = new Vector3[] { p1 };
p1 = new Vector3(2,2,2);
This doesn't change the contents of the array for 2 reasons (sort of). First reason is that Vector3 is a struct, not a class, so its passed as a value, not as reference (more on that later).
The other reason this doesn't work is (lets pretend Vector3 is a ref type): You declare p1 - a reference to a memory address that holds vector (-1,0,0). You put a reference to that address into your array. All fine so far. The last line doesn't change the data that is stored in your memory address; it assigns p1 to point to a different memory address that holds another vector (2,2,2). Your array still holds a reference to the old address with (-1,0,0).
So in theory this should work better:
Vector3 p1 = new Vector3(-1, 0, 0);
Vector3[] vertices = new Vector3[] { p1 };
p1.x = 2;
p1.y = 2;
p1.z = 2;
Debug.Log(vertices[0]);
Now we are changing the data inside the object the memory address points to - p1 and vecrtices[0] reference the same address so it should work? If you try that, it doesn't, which is due to the second issue that Vector3 isn't a reference type.
Because of that, when you put p1 into the array, the array doesn't get a reference to the original vector but rather gets a copy of the vector that just has the same value(s).
That was basic programming, and a big reason to why the mesh isn't changing: You aren't actually changing the vertices because changing p1, p2 and p3 don't change the contents of the mesh.vertices array.
A third issue is that even is you could change the contents of the mesh.vertices array by changing individual Vectors in it, Unity needs to know the changes so it can pass the new values to the rendering pipeline and eventually make the change visible on your screen.
That's why, if you want the mesh to change, you need to assign a new array into 'mesh.vertices'. It's not an array variable but a property that not only takes in the new array, but also takes the necessary steps to make the changes visible on screen.
The same applies to all the other properties like mesh.normals etc.
So finally, you should do it something like:
Vector3[] vertices = new Vector3[]
{
new Vector3(-1, 0, 0),
new Vector3(0, 2, 0),
new Vector3(1, 0, 0)
};
int[] triangles = new int[]
{
0,1,2
};
Vector3[] normals = new Vector3[]
{
Vector3.forward,
Vector3.forward,
Vector3.forward
};
private MeshFilter mf;
void Start() {
mf = GetComponent<MeshFilter>();
mf.mesh = new Mesh();
mf.mesh.vertices = vertices;
mf.mesh.triangles = triangles;
mf.mesh.normals = normals;
}
void Update() {
if (Input.GetKeyDown("up")) {
mf.mesh.vertices = new[]
{
Random.insideUnitSphere,
Random.insideUnitSphere,
Random.insideUnitSphere
};
Debug.Log("Up is pressed.");
}
}
Thank you! Learned a lot about the value type and reference type! I am new to code and have to work harder. Now I am thinking if I want to play a song, and use the loudness to change the meshes like this: https://vimeo.com/groups/308518/videos/79533030 Does that mean I have to keep assigning new arrays into 'mesh.vertices'?
Does that mean I have to keep assigning new arrays into 'mesh.vertices'?
We have to be very careful with word choices here :) Yes, you have to assign an array to mesh.vertices in order to see any changes on screen, so the only way to save performance is not to make "new arrays". You should make one array like in the example, put new Vectors into it or change them, and then assign this array with changed contents into mesh.vertices every time.
For example
Vector3[] verts = new Vector3[] {
new Vector3(1, 1, 1),
new Vector3(2, 2, 2)
};
void Start () {
verts[0].x = 4;
verts[0].y++;
verts[0].z *= 10;
verts[1] = new Vector3(3, 3, 3);
Debug.Log(verts[0] + " - " + verts[1]);
mesh.vertices = verts;
}
It's definitely not a big deal especially in a program like this, but using the "new" keyword means you are creating something new into memory. And everything "new" will most likely have to be garbage collected eventually when you are done using it (can be slow).
That being said, don't be afraid of the "new" keyword. You have to use it all the time. But this is just an obvious case where you can create just one array and keep re-using it ins$$anonymous$$d of making new ones.
Some good points, but while creating new objects (classes) certainly can and will trigger garbage collection, value types are created on the stack and their memory deallocation isn't handled by garbage collection.
Answer by mikelortega · Jan 19, 2016 at 08:09 AM
I would create a new function, for instance UpdateTriangleMesh(), and put your code there.
Then, call this function in Start.
void Start()
{
UpdateTriangleMesh();
}
And call this function every time you press "up", inside the if, this way it will be called only when you press that key.
void Update()
{
if (Input.GetKeyDown("up"))
UpdateTriangleMesh();
}