In my application (based on Unity3D) meshes are loaded at runtime. In some meshes all faces are flipped (see picture 1). I tried to implement a simple algorithm that calculates the center of all vertices and checks if the normal points towards the center. If this is the case this face should be flipped. The problem is that (as you can see in picture 2) the algorithm only flipped a few faces when all faces were pointing in the wrong direction. The flipped meshes are almost exclusively stairs (if that helps).
I would be grateful if someone shows me my error or knows a better way. In Blender there is the function "recalculate normals", but I didn't understand it correctly and probably it is a too complicated solution for my problem.
Here the algorithm (C#) and the pictures:
public static class FixMeshUtil
{
public static void FixNormals(Mesh mesh)
{
if(mesh.vertexCount != mesh.normals.Length)
mesh.RecalculateNormals();
Vector3[] normals = mesh.normals;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
Vector3 center = CenterPoint(vertices);
for(int i = 0; i < triangles.Length; i += 3)
{
Vector3 v1 = vertices[triangles[i]];
Vector3 v2 = vertices[triangles[i + 1]];
Vector3 v3 = vertices[triangles[i + 2]];
Vector3 n1 = normals[triangles[i]];
Vector3 n2 = normals[triangles[i + 1]];
Vector3 n3 = normals[triangles[i + 2]];
Vector3 calcNormal = CalculateNormal(v1, v2, v3);
if(!WithinTolerance(n1))
n1 = calcNormal;
if(!WithinTolerance(n2))
n2 = calcNormal;
if(!WithinTolerance(n3))
n3 = calcNormal;
if(IsFacingInwards(calcNormal, center))
Array.Reverse(triangles, i, 3);
}
mesh.normals = normals;
mesh.triangles = triangles;
}
private static Vector3 CenterPoint(Vector3[] vertices)
{
Vector3 center = Vector3.zero;
for(int i = 1; i < vertices.Length; ++i)
center += vertices[i];
return center / vertices.Length;
}
private static bool WithinTolerance(Vector3 normal) => normal.magnitude > 0.001f;
private static bool IsFacingInwards(Vector3 normal, Vector3 center) =>
Vector3.Dot((normal - center).normalized, normal.normalized) < 0f;
private static Vector3 CalculateNormal(Vector3 a, Vector3 b, Vector3 c)
{
Vector3 v1 = b - a;
Vector3 v2 = c - a;
return new Vector3
(
(v1.y * v2.z) - (v1.z * v2.y),
(v1.z * v2.x) - (v1.x * v2.z),
(v1.x * v2.y) - (v1.y * v2.x)
).normalized;
}
}
Update: Thanks to Thibault Cimic the code works by changing the IsFacingInwards function to:
Vector3 midPoint = center - ((v1 + v2 + v3) / 3);
//...
if(IsFacingInwards(calcNormal, midPoint))
//...
private static bool IsFacingInwards(Vector3 normal, Vector3 direction) =>
Vector3.Dot(direction.normalized, normal.normalized) > 0f;
I'm not sure you're giving the right mathematical object to IsFacingInwards();
Put IsFacingInwards(calcNormal,center-p) where p is one of the vertice that compose the edge from which you want the outwards normal maybe ?