- Home /
What is the "correct" what to make a procedural mesh script
I'd like to make a procedural mesh that can be changed at runtime and edit time.
I'm using [ExecuteInEditMode]
and I get a warning
Instantiating mesh due to calling MeshFilter.mesh during edit mode. This will leak meshes. Please use MeshFilter.sharedMesh instead.
UnityEngine.MeshFilter:get_mesh()
ProceduralPlane:Start() (at Assets/GMan/ProceduralPlane.cs:32)
I tried changing mesh
to sharedMesh
but there is no sharedMesh. Should I create a Mesh
and attach it to sharedMesh
? I tried it, it didn't seem to work. I get a different warning about cleaning up meshes. I also tried creating the mesh in the constructor. I still get the same message about leaked meshes.
The question is, what's the correct way to do this? It would be nice if the docs mentioned the right way.
Note: A mesh that is created once (like some script you run and give parameters, click 'create' and it makes a mesh is NOT what I want). I want a mesh I can adjust in resolution after it has been placed in the scene and while I'm editing.
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class ProceduralPlane : MonoBehaviour {
[Range(0, 100)]
public float width = 1.0f;
[Range(0, 100)]
public float depth = 1.0f;
[Range(1, 100)]
public int subDivisionsAcross = 1;
[Range(1, 100)]
public int subDivisionsDown = 1;
private Mesh m_mesh;
private int m_oldSubdivisionsAcross;
private int m_oldSubdivisionsDown;
private float m_oldWidth;
private float m_oldDepth;
// Use this for initialization
void Start() {
MeshFilter mf = GetComponent<MeshFilter>();
if (mf == null) {
gameObject.AddComponent<MeshFilter>();
}
mf = GetComponent<MeshFilter>();
m_mesh = mf.mesh;
UpdateMesh();
}
void Update() {
if (m_oldSubdivisionsAcross != subDivisionsAcross ||
m_oldSubdivisionsDown != subDivisionsDown ||
m_oldWidth != width ||
m_oldDepth != depth) {
UpdateMesh();
}
}
private void UpdateMesh() {
int vertsAcross = subDivisionsAcross + 1;
int vertsDown = subDivisionsDown + 1;
int numVertices = vertsAcross * vertsDown;
int numQuads = subDivisionsAcross * subDivisionsDown;
Vector3[] positions = new Vector3[numVertices];
Vector3[] normals = new Vector3[numVertices];
Vector2[] uvs = new Vector2[numVertices];
Color32[] colors = new Color32[numVertices];
int[] triangles = new int[numQuads * 6];
for (int y = 0; y <= subDivisionsDown; ++y) {
for (int x = 0; x <= subDivisionsAcross; ++x) {
int offset = y * vertsAcross + x;
float u = (float)x / (float)subDivisionsAcross;
float v = (float)y / (float)subDivisionsDown;
float px = (u - 0.5f) * width;
float pz = (v - 0.5f) * depth;
positions[offset] = new Vector3(px, 0.0f, pz);
normals[offset] = Vector3.up;
uvs[offset] = new Vector2(u, v);
colors[offset] = new Color32(255, 255, 255, 255);
}
}
for (int y = 0; y < subDivisionsDown; ++y) {
int yoff0 = (y + 0) * vertsAcross;
int yoff1 = (y + 1) * vertsAcross;
for (int x = 0; x < subDivisionsAcross; ++x) {
int offset = (y * subDivisionsAcross + x) * 6;
triangles[offset + 0] = yoff0 + x;
triangles[offset + 2] = yoff0 + x + 1;
triangles[offset + 1] = yoff1 + x;
triangles[offset + 3] = yoff1 + x;
triangles[offset + 5] = yoff0 + x + 1;
triangles[offset + 4] = yoff1 + x + 1;
}
}
// we have to set no triangles before setting the verts
// to avoid bounds checking errors.
m_mesh.triangles = new int[0];
m_mesh.vertices = positions;
m_mesh.normals = normals;
m_mesh.uv = uvs;
m_mesh.colors32 = colors;
m_mesh.triangles = triangles;
m_mesh.RecalculateBounds();
m_oldSubdivisionsAcross = subDivisionsAcross;
m_oldSubdivisionsDown = subDivisionsDown;
m_oldWidth = width;
m_oldDepth = depth;
}
}
Note: if you want to try the script, create a empty GameObject, attach a MeshRenderer component, set a material, then add this script.