/////////////////////////////////////////////////////////////////////////// #region [SetInMeshFilter] /** * Convert a BMesh into a Unity Mesh and set it in the provided MeshFilter * WARNING: Only works with tri or quad meshes! * read attributes uv, uv2 from vertices and materialId from faces. * * NB: UVs are read from vertices, because in a Unity mesh when two face * corners have different UVs, they are different vertices. If you worked * with UVs as Loop attributes, you must first split points and migrate UVs * to vertex attributes. */ public static void SetInMeshFilter(BMesh mesh, MeshFilter mf) { // Points Vector2[] uvs = null; Vector2[] uvs2 = null; Vector3[] normals = null; Color[] colors = null; Vector3[] points = new Vector3[mesh.vertices.Count]; if (mesh.HasVertexAttribute("uv")) { uvs = new Vector2[mesh.vertices.Count]; } if (mesh.HasVertexAttribute("uv2")) { uvs2 = new Vector2[mesh.vertices.Count]; } if (mesh.HasVertexAttribute("normal")) { normals = new Vector3[mesh.vertices.Count]; } if (mesh.HasVertexAttribute("color")) { colors = new Color[mesh.vertices.Count]; } int i = 0; foreach (var vert in mesh.vertices) { vert.id = i; points[i] = vert.point; if (uvs != null) { var uv = vert.attributes["uv"] as FloatAttributeValue; uvs[i] = new Vector2(uv.data[0], uv.data[1]); } if (uvs2 != null) { var uv2 = vert.attributes["uv2"] as FloatAttributeValue; uvs2[i] = new Vector2(uv2.data[0], uv2.data[1]); } if (normals != null) { var normal = vert.attributes["normal"] as FloatAttributeValue; normals[i] = normal.AsVector3(); } if (colors != null) { var color = vert.attributes["color"] as FloatAttributeValue; colors[i] = color.AsColor(); } ++i; } // Triangles int maxMaterialId = 0; bool hasMaterialAttr = mesh.HasFaceAttribute("materialId"); if (hasMaterialAttr) { foreach (var f in mesh.faces) { maxMaterialId = Mathf.Max(maxMaterialId, f.attributes["materialId"].asInt().data[0]); } } int[] tricounts = new int[maxMaterialId + 1]; foreach (var f in mesh.faces) { Debug.Assert(f.vertcount == 3 || f.vertcount == 4, "Only meshes with triangles/quads can be converted to a unity mesh"); int mat = hasMaterialAttr ? f.attributes["materialId"].asInt().data[0] : 0; tricounts[mat] += f.vertcount - 2; } int[][] triangles = new int[maxMaterialId + 1][]; for (int mat = 0; mat < triangles.Length; ++mat) { triangles[mat] = new int[3 * tricounts[mat]]; tricounts[mat] = 0; } // from now on tricounts[i] is the index of the next triangle to fill in the i-th triangle list foreach (var f in mesh.faces) { int mat = hasMaterialAttr ? f.attributes["materialId"].asInt().data[0] : 0; Debug.Assert(f.vertcount == 3 || f.vertcount == 4); { var l = f.loop; triangles[mat][3 * tricounts[mat] + 0] = l.vert.id; l = l.next; triangles[mat][3 * tricounts[mat] + 2] = l.vert.id; l = l.next; triangles[mat][3 * tricounts[mat] + 1] = l.vert.id; l = l.next; ++tricounts[mat]; } if (f.vertcount == 4) { var l = f.loop.next.next; triangles[mat][3 * tricounts[mat] + 0] = l.vert.id; l = l.next; triangles[mat][3 * tricounts[mat] + 2] = l.vert.id; l = l.next; triangles[mat][3 * tricounts[mat] + 1] = l.vert.id; l = l.next; ++tricounts[mat]; } } // Apply mesh Mesh unityMesh = new Mesh(); mf.mesh = unityMesh; unityMesh.vertices = points; if (uvs != null) { unityMesh.uv = uvs; } if (uvs2 != null) { unityMesh.uv2 = uvs2; } if (normals != null) { unityMesh.normals = normals; } if (colors != null) { unityMesh.colors = colors; } unityMesh.subMeshCount = triangles.Length; // Fix an issue when renderer has more materials than there are submeshes var renderer = mf.GetComponent <MeshRenderer>(); if (renderer) { unityMesh.subMeshCount = Mathf.Max(unityMesh.subMeshCount, renderer.materials.Length); } for (int mat = 0; mat < triangles.Length; ++mat) { unityMesh.SetTriangles(triangles[mat], mat); } if (normals == null) { unityMesh.RecalculateNormals(); } }