- Home /
Textures are dark after merging vertices
I have a script that takes multiple quads and combine them into one mesh. Instead of having so many unneeded vertices in the combined mesh I used another function to merge nearby vertices to reduce polycount. This works flawlessly in the editor, but not for Samsung phones. If the material uses a bump/normal map then the texture becomes dark. For any other phones it works fine but not Samsung. Is there a way for me to debug this bug? Is there something special about Samsung phones that I need to be aware of? I have a Samsung Note 3 and Note 4 Edge.
Here's the function used for merging the meshes:
public static GameObject combineMeshes(GameObject[] meshes, string name, bool sameMaterial, bool removeOrig) {
if (meshes.Length == 0) return null;
GameObject combinedFrags = new GameObject(name);
combinedFrags.AddComponent<MeshFilter>();
combinedFrags.AddComponent<MeshRenderer>();
// Get all meshFilters
List<MeshFilter> meshFilters = new List<MeshFilter>();
List<Material> materials = new List<Material>();
foreach (GameObject mesh in meshes) { // Remove empty values
if (mesh) {
MeshFilter meshFilter = mesh.GetComponent<MeshFilter> ();
if (meshFilter) {
meshFilters.Add(meshFilter);
MeshRenderer meshRenderer = mesh.GetComponent<MeshRenderer> ();
if (meshRenderer) {
foreach(Material material in meshRenderer.sharedMaterials) {
if (!sameMaterial && !materials.Contains(material) || materials.Count == 0) materials.Add(material);
}
}
}
}
}
if (removeOrig) {
foreach (GameObject mesh in meshes) Destroy(mesh);
}
CombineInstance[] combine = new CombineInstance[meshFilters.Count];
// Key: shared mesh instance ID, Value: arguments to combine meshes
Dictionary<int, List<CombineInstance>> helper = new Dictionary<int, List<CombineInstance>>();
// Build combine instances for each type of mesh
foreach (MeshFilter m in meshFilters) {
List<CombineInstance> tmp;
if (!helper.TryGetValue(m.sharedMesh.GetInstanceID(), out tmp)) {
tmp = new List<CombineInstance>();
helper.Add(m.sharedMesh.GetInstanceID(), tmp);
}
CombineInstance ci = new CombineInstance();
ci.mesh = m.sharedMesh;
ci.transform = m.transform.localToWorldMatrix;
tmp.Add(ci);
}
// Combine meshes and build combine instance for combined meshes
List<CombineInstance> list = new List<CombineInstance>();
foreach (var e in helper) {
Mesh m = new Mesh();
m.CombineMeshes(e.Value.ToArray(), sameMaterial);
CombineInstance ci = new CombineInstance();
ci.mesh = m;
list.Add(ci);
}
// And now combine everything
Mesh result = new Mesh();
result.name = name;
result.CombineMeshes(list.ToArray(), sameMaterial, false);
combinedFrags.GetComponent<MeshFilter> ().mesh = result;
combinedFrags.GetComponent<Renderer>().sharedMaterials = materials.ToArray();
// It is a good idea to clean unused meshes now
foreach (CombineInstance m in list) Destroy(m.mesh);
return combinedFrags;
}
Here's the function for merging nearby vertices:
public static void autoWeld(Mesh mesh, float threshold, float bucketStep) {
Vector3[] oldVertices = mesh.vertices;
Vector3[] newVertices = new Vector3[oldVertices.Length];
int[] old2new = new int[oldVertices.Length];
int newSize = 0;
// Find AABB
Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
for (int i = 0; i < oldVertices.Length; i++) {
if (oldVertices[i].x < min.x) min.x = oldVertices[i].x;
if (oldVertices[i].y < min.y) min.y = oldVertices[i].y;
if (oldVertices[i].z < min.z) min.z = oldVertices[i].z;
if (oldVertices[i].x > max.x) max.x = oldVertices[i].x;
if (oldVertices[i].y > max.y) max.y = oldVertices[i].y;
if (oldVertices[i].z > max.z) max.z = oldVertices[i].z;
}
// Make cubic buckets, each with dimensions "bucketStep"
int bucketSizeX = Mathf.FloorToInt((max.x - min.x) / bucketStep) + 1;
int bucketSizeY = Mathf.FloorToInt((max.y - min.y) / bucketStep) + 1;
int bucketSizeZ = Mathf.FloorToInt((max.z - min.z) / bucketStep) + 1;
List<int>[,,] buckets = new List<int>[bucketSizeX, bucketSizeY, bucketSizeZ];
// Make new vertices
for (int i = 0; i < oldVertices.Length; i++) {
// Determine which bucket it belongs to
int x = Mathf.FloorToInt((oldVertices[i].x - min.x) / bucketStep);
int y = Mathf.FloorToInt((oldVertices[i].y - min.y) / bucketStep);
int z = Mathf.FloorToInt((oldVertices[i].z - min.z) / bucketStep);
// Check to see if it's already been added
if (buckets[x, y, z] == null) buckets[x, y, z] = new List<int> (); // Make buckets lazily
for (int j = 0; j < buckets[x, y, z].Count; j++) {
Vector3 to = newVertices[buckets[x, y, z][j]] - oldVertices[i];
if (Vector3.SqrMagnitude(to) < threshold) {
old2new[i] = buckets[x, y, z][j];
goto skip;
}
}
// Add new vertex
newVertices[newSize] = oldVertices[i];
buckets[x, y, z].Add(newSize);
old2new[i] = newSize;
newSize++;
skip:;
}
// Make new triangles
int[] oldTris = mesh.triangles;
int[] newTris = new int[oldTris.Length];
for (int i = 0; i < oldTris.Length; i++) newTris[i] = old2new[oldTris[i]];
Vector3[] finalVertices = new Vector3[newSize];
for (int i = 0; i < newSize; i++) finalVertices[i] = newVertices[i];
mesh.Clear();
mesh.SetVertices(finalVertices.ToList());
mesh.SetTriangles(newTris, 0);
mesh.RecalculateNormals();
}
Here's the results in editor:
Here's the result for Samsung:
Rather than using RecalculateNormals(), have you tried defining them manually to see whether that might be what's falling through?
I've tried using these methods to recalculate normals:
http://sche$$anonymous$$gdeveloper.com/2014/10/17/better-method-recalculate-normals-unity/
and
http://docs.unity3d.com/ScriptReference/$$anonymous$$esh-normals.html