three.jsreverseflipnormalsface

How to detect reversed faces (flipped normals)?


I need to load several STL files in a scene, some of which have reversed/flipped faces. This can be easily fixed (visually) using a double-sided material, but what I try is to visualize the wrong faces, switching the material index from blue to red.

If I understand, a mirrored/flipped/reversed face is being drawn clockwise instead of counter-clockwise. But are we talking about the three vectors that form the face? If so, the winding order is the order set at face.a, face.b and face.c? Or should I have to compare the face index value to know the order the face is drawn?

I set up a JSFiddle to ilustrate the problem: this STL model has 10 faces, and 3 of them are reversed or flipped. Using a FaceNormalsHelper you can see that those 3 normal helpers point to the inside of the model, instead outside. I´ve tried 6 different ways to check this, some of them are extracted from the code at computeFaceNormals(), with no luck. I´ve also examined the FaceNormalsHelper to see how it works, but it simply draws a line from the face centroid in the normals direction, but it never gets to calculate if it´s pointing inside or outside.

(Note: in order to change from one method to another, you must change the variable at the start of the script. Method 1 manually overrides these three faces material index, just to visualize the wrong ones (as a validation of all the other methods). No other one gets the good results.. But method 2 and 3 are really really close to it, although somehow two other faces are detected as flipped. I wonder if the face angle has something to do with this)

EDIT: The method suggested by @manthrax works perfectly, comparing edges of each face with all the other ones. If the edge is drawn the same way (example: face 1 edge a-b matches face 2 edge a-b, instead of b-a), it´s flipped. Basically, if a face has two or more edges marked as flipped, we can say the whole face is flipped. Check the JSFiddle to see it working.

enter image description here

function paintFlippedFaces(faceCheck){
    for(var x=0; x<Object.keys(faceCheck).length; x++){
    var flipCounter = 0;
    if(faceCheck[x]['a-b']) flipCounter++;
    if(faceCheck[x]['b-c']) flipCounter++;
    if(faceCheck[x]['c-a']) flipCounter++;        
    console.log("face " + x + " has " +flipCounter+ " flipped edges" );
    if(flipCounter >= 2){
      geometry.faces[x].materialIndex = 1;
      //geometry.faces.elementsNeedUpdate = true;
    }
  } 
}

Solution

  • First off, "flipped" faces are totally subjective. You need to decide on some parameters. For a convex object, there is a non-subjective definition.. for instance that the face normal, dotted with one of the face vertices, is > 0.

    For non-convex objects you can make some guesses based on the winding order of the vertices, and whether any 2 edges contains the same ordering, implying that you have 1 front facing and 1 back facing triangle sharing an edge. (Which of those triangles is flipped, isn't necessarily identifiable in the non-convex case though.)

    First, you'll have to .mergeVertices on your geometry, so that vertices are shared across faces... I think stl defines unique vertices for every triangle, so there's no concept of an "edge" that is shared by two faces.

    Then you can loop through each face.. and basically any 2 edges that share the same 2 vertices, should have opposition ordering, or that faces are facing opposite each other..

    So if 2 faces have an edge like 3,2 and 2,3 then they are both facing the same direction.. and the faces are consistent.

    If two different faces both have an edge that are 3,2 and 3,2, then they are facing opposite directions, and you could flag both faces as possibly being flipped. The faces that get flagged the most times are the ones that are probably actually flipped.

    Those are the best guess tests I can think of to determine inside/outside, but it's a tricky problem.

    Blender has a couple "recalculate normals" methods for recomputing "inside"/"outside" that I think operate on a similar principle.. so you could also look at those.