- Home /
Vertex color emptied after instanciating mesh at runtime (build only)
I have a project where I'm using vertex colours as data to procedurally stretch my meshes, ie red goes left, green: forward, blue: up, I don't use them to render at all. At run-time I load the mesh and modify the vertices using the colour information. This works perfectly in debug, but in a built version the color data in the instanciated meshes is empty.
If I manually place a the meshes in the scene with a material that uses vertex colours this seems to force unity to know it has colours and it works fine. However this is an ugly hack, is there something I'm missing?
It definitely could be a bug. Are you using GPU instancing? Also, when you modify the vertex colours, are you doing it post-instantiation (with different vertex colours per instance), or are the vertex colours shared between all copies of the mesh?
Here's an example, just a single script and a basic model with vertex colours, the whole project was a bit big to upload, I've just put this together quickly to demonstrate the scenario. You'll notice it works fine in debug, but when you build it, it throws an out of bounds exception because the mesh.colors is empty.
Note: you need to turn on Read/Write Enabled on the mesh
Here's the code again to make it more visible:
private void Stretch$$anonymous$$esh()
{
var instance = Instantiate($$anonymous$$eshObject);
var meshes = instance.GetComponentsInChildren<$$anonymous$$eshFilter>().Select(v => v.mesh);
var moveDist = 10f;
foreach (var mesh in meshes)
{
var verts = mesh.vertices;
var colors = mesh.colors;
for (var i = 0; i < verts.Length; i++)
{
// On build this throws an index out of range exception because the colours are empty
verts[i].x += moveDist * colors[i].r;
}
var new$$anonymous$$esh = new $$anonymous$$esh
{
vertices = verts,
triangles = mesh.triangles,
colors = mesh.colors,
normals = mesh.normals,
tangents = mesh.tangents,
uv = mesh.uv,
};
new$$anonymous$$esh.RecalculateBounds();
var go = new GameObject("Test");
var filter = go.AddComponent<$$anonymous$$eshFilter>();
var renderer = go.AddComponent<$$anonymous$$eshRenderer>();
filter.mesh = new$$anonymous$$esh;
renderer.material = Test$$anonymous$$aterial;
}
}
Ah, my misunderstanding. You're absolutely right, it would appear as though Unity only registers mesh colours if they are going to be immediately used in rendering; if the material doesn't use mesh colours, it seems to just skip loading them entirely. The only real workaround I can suggest without having to manually place the objects is to just go into the mesh import settings and override the default material to something that uses vertex colours. It has the same effect in forcing Unity to recognise the mesh fully, but you don't need to change anything in your scene. I can only guess this has something to do with Unity wanting to pass as little data into the shader as possible, and maybe getting a bit carried away in their assumptions on how meshes are used. $$anonymous$$ight be worth reporting as a bug. Aside from that, I will say - could you not do this all in a shader anyway? It would be a lot more efficient than directly modifying the mesh.
$$anonymous$$akes sense, however I don't think a shader will work too well for this, I'm building lots of these with different values passed in and they are combined into meshes with the same materials in the end so they're pretty cheap once the procedural generation has finished. After some more playing I think I'll just move toward using bones ins$$anonymous$$d, they're a little easier to setup and more flexible in the end plus unity has a method to bake them down to a mesh.
You do realise that this line:
var instance = Instantiate($$anonymous$$eshObject);
does NOT instantiate your meshes at all. All your mesh filters will still use their original meshes. However this part:
.Select(v => v.mesh);
will implicitly duplicate the mesh for this meshfilter instance specifically. Have you tried to actually instantiate the shared$$anonymous$$esh manually for each $$anonymous$$eshFilter? Something like that:
var meshFilters = instance.GetComponentsInChildren<$$anonymous$$eshFilter>();
var moveDist = 10f;
foreach (var meshFilter in meshFilters)
{
var mesh = Instantiate(meshFilter.shared$$anonymous$$esh);
meshFilter.shared$$anonymous$$esh = mesh;
var verts = mesh.vertices;
var colors = mesh.colors;