Thanks for your answer, I’m back with more insight and some code. I stripped down my implementation to what was basically the code sample in the docs, and could confirm that this one at least works. The issue I’m facing seems to stem from me using the advanced mesh api to set the vertex buffer layout directly, which I need/want to do to reduce precision on some channels to improve runtime memory usage. After doing that, SetMeshCompression doesn’t do anything anymore, but also does not log any errors/warnings.
Here’s basically what I’m doing:
Method 1: Correct vertex buffer precision, but no compression
private static void CopyAndCompress(Mesh mesh, ModelImporterMeshCompression modelImporterMeshCompression)
{
//get original mesh data
var indexCount = Enumerable.Range(0, mesh.subMeshCount).Sum(i => mesh.GetSubMesh(i).indexCount);
using var datas = Mesh.AcquireReadOnlyMeshData(mesh);
var data = datas[0];
var array = data.GetVertexData<GenericVertexBufferData9>(); //unmanaged struct holding 9x4 bytes of data, hardcoded for this test
var indices = data.GetIndexData<ushort>(); //format is hardcoded for this test
var mesh2 = new Mesh{name = mesh.name+"2"};
//set vertex & index buffer data
mesh2.SetVertexBufferParams(mesh.vertexCount, Enumerable.Range(0, mesh.vertexAttributeCount).Select(mesh.GetVertexAttribute).ToArray());
mesh2.SetIndexBufferParams(indexCount, mesh.indexFormat);
mesh2.SetVertexBufferData(array, 0,0, mesh.vertexCount);
mesh2.SetIndexBufferData(indices,0,0, data.GetSubMesh(0).indexCount);
mesh2.SetSubMeshes(Enumerable.Range(0, mesh.subMeshCount).Select(mesh.GetSubMesh).ToArray());
//compress and create
MeshUtility.SetMeshCompression(mesh2, modelImporterMeshCompression);
AssetDatabase.CreateAsset(mesh2, AssetDatabase.GetAssetPath(mesh).Replace(".mesh",2+".mesh"));
}
This method copies a mesh fully utilizing the advanced mesh api. The original mesh already has reduced precision on some attributes, like this:
The produced mesh copy retains the vertex buffer layout with the correct precision. However, it is not compressed (same size on disk as original).
Method 2: Incorrect vertex buffer precision, but compression works
private static void CopyAndCompress2(Mesh mesh, ModelImporterMeshCompression modelImporterMeshCompression)
{
//copy data arrays over one by one, let unity figure out the layout
var mesh2 = new Mesh
{
name = mesh.name + "2",
vertices = mesh.vertices,
triangles = mesh.triangles,
normals = mesh.normals,
boneWeights = mesh.boneWeights,
colors = mesh.colors,
};
var uvs4 = new List<Vector4>();
var uvs3 = new List<Vector3>();
var uvs2 = new List<Vector2>();
//copy all texcoords over that exist, with their correct dimension
for (var i = 0; i < 8; i++)
{
if (!mesh.HasVertexAttribute(VertexAttribute.TexCoord0 + i))
continue;
switch (mesh.GetVertexAttributeDimension(VertexAttribute.TexCoord0 + i))
{
case 2:
mesh.GetUVs(i, uvs2);
mesh2.SetUVs(i, uvs2);
break;
case 3:
mesh.GetUVs(i, uvs3);
mesh2.SetUVs(i, uvs3);
break;
case 4:
mesh.GetUVs(i, uvs4);
mesh2.SetUVs(i, uvs4);
break;
}
}
//compress and create
MeshUtility.SetMeshCompression(mesh2, modelImporterMeshCompression);
AssetDatabase.CreateAsset(mesh2, AssetDatabase.GetAssetPath(mesh).Replace(".mesh",2+".mesh"));
}
This method copies a mesh the traditional way. Since there is no way to alter the precision of vertex attributes on the new mesh, it automatically assumes full precision on all of them. The result is a significantly larger vertex buffer:
But at least, compression actually works here, so on disk the serialized mesh is smaller.
I’ve also tried some hybrid approach where I copy the arrays one by one and apply the vertex buffer params afterwards to force lower precision on the copied data, like this:
Method 3: Hybrid
private static void CopyAndCompress3(Mesh mesh, ModelImporterMeshCompression modelImporterMeshCompression)
{
//copy data arrays over one by one
var mesh2 = new Mesh
{
name = mesh.name + "2",
vertices = mesh.vertices,
triangles = mesh.triangles,
normals = mesh.normals,
boneWeights = mesh.boneWeights,
colors = mesh.colors,
};
var uvs4 = new List<Vector4>();
var uvs3 = new List<Vector3>();
var uvs2 = new List<Vector2>();
//copy all texcoords over that exist, with their correct dimension
for (var i = 0; i < 8; i++)
{
if (!mesh.HasVertexAttribute(VertexAttribute.TexCoord0 + i))
continue;
switch (mesh.GetVertexAttributeDimension(VertexAttribute.TexCoord0 + i))
{
case 2:
mesh.GetUVs(i, uvs2);
mesh2.SetUVs(i, uvs2);
break;
case 3:
mesh.GetUVs(i, uvs3);
mesh2.SetUVs(i, uvs3);
break;
case 4:
mesh.GetUVs(i, uvs4);
mesh2.SetUVs(i, uvs4);
break;
}
}
//set vertex and index buffer layout after copying the arrays
mesh2.SetVertexBufferParams(mesh.vertexCount, Enumerable.Range(0, mesh.vertexAttributeCount).Select(mesh.GetVertexAttribute).ToArray());
var indexCount = Enumerable.Range(0, mesh.subMeshCount).Sum(i => mesh.GetSubMesh(i).indexCount);
mesh2.SetIndexBufferParams(indexCount, mesh.indexFormat);
mesh2.SetSubMeshes(Enumerable.Range(0, mesh.subMeshCount).Select(mesh.GetSubMesh).ToArray());
//create and compress
MeshUtility.SetMeshCompression(mesh2, modelImporterMeshCompression);
AssetDatabase.CreateAsset(mesh2, AssetDatabase.GetAssetPath(mesh).Replace(".mesh",2+".mesh"));
}
But that produces the exact same result as method 1.
Same goes for saving the asset first and loading it again from the asset database before applying compression and saving it again - works if the mesh was created with method 2 (one by one array copy), but fails if the mesh was created with the advanced mesh api.
I’ve also tried fitting in mesh.Optimize and removed AssetDatabase.SaveAssets as you suggested (“optimize mesh data” in settings was already enabled), makes no difference sadly. I also don’t think there’s a mesh importer, just the model importer which does not process native mesh assets (at least I couldn’t find one). I’ve looked at the native format importer but that doesn’t have any options I could use.
For now it just seems that using the advanced mesh api locks you out from using compression, unless I’m still missing something. If anyone knows more, please let me know.
That sounds interesting, but I haven’t used a scripted importer before. What would be the idea behind it? I’m writing my own file format with my own compression, and then my own scripted importer to tell unity how to deserialize a mesh from that?