Selecting all meshes and flipping only those with inward facing normals

I am trying to figure out how to use the Mesh.SolidOrientation method in order to script something that will allow me to select all of the separate mesh objects in a window, recognize the solid mesh objects with inward facing normals, and flip them.

My real question is if there is a way to isolate mesh objects by which direction their normals are facing?

Hi @Mprice?

There might be a build-in method for this (?), but if not, you could for instance slightly move a single vertex of each mesh in the normal direction of an adjacent face, and then check if it is included inside the watertight mesh. If it is, the face normal is pointing inwards and you flip all normals.

In which programming language are you looking to write this?

The Mesh.SolidOrientation function seems to do what you describe. Here is a python snippet that selects the ‘inverted’ meshes:

import scriptcontext as sc
import rhinoscriptsyntax as rs
import Rhino as R

guids = rs.GetObjects('select objects')
for guid in guids:
    mesh = rs.coercemesh(guid)
    if mesh:
        # print check
        print guid, mesh.SolidOrientation()
        if mesh.SolidOrientation() < 0:
            # select the inverted normal meshes
            sc.doc.Objects.Select(guid, True, True)
2 Likes

Thanks @nathancoatney !

There appears to be a bug with mesh.SolidOrientation
The script that you provided does what I need it to, but it feels like a one and done process. I can make it work though.

Here is a previous post where he points out the same issue.

Below is the python script we created here, it does the exact same thing as yours.

import rhinoscriptsyntax as rs

objects = rs.GetObjects("Mesh", 32, True, True) 

for object in objects: 
if not object: continue

mesh = rs.coercemesh(object) 

if (mesh.SolidOrientation() == -1): 
   rs.SelectObject(object)

Once the objects have been assigned a value of -1, it is selected, and I am able to click the ‘Flip Mesh Normals’ button to manually flip them so the normals face outwards. However the catch is, once a mesh has been assigned either a +1, -1, or 0, it remains that way. No amount of changes appear to change its mind. (This being pointed out in the other post).
If this is a bug it really should be addressed. Otherwise, I would love to know how Rhino assigns the SolidOrientation value and why it is permanent.

Hi @mprice, yes. This is the reason why i did not answer your question. As long as the Mesh.SolidOrientation bug is not fixed, there is really no easy way to do what you want apart from this.

I do not think this can work reliably if the normals are eg. not properly unified. You’ll have to use fairly high normal offset distances to make Mesh.IsPointInside method do it’s thing which will also fail on solid meshes having thin areas. The method Mesh.IsPointInside has known problems unfortunately.

_
c.

1 Like

Ah, wasn’t aware of the lower level bugs. Not to be defeated, here is another approach that may work:

Since SolidOrientation works once, use it to find ‘inverted’ meshes.
Make a duplicate new mesh with correct orientation.
Replace ‘inverted’ mesh.

import scriptcontext as sc
import rhinoscriptsyntax as rs
import Rhino as R

guids = rs.GetObjects('select objects')
for guid in guids:
    mesh = rs.coercemesh(guid)
    if mesh:
        # print check
        print guid, mesh.SolidOrientation()
        if mesh.SolidOrientation() < 0:
            # make a new mesh
            new_mesh = R.Geometry.Mesh()
            # copy over all the verts from inverted mesh
            for v in mesh.Vertices:
                new_mesh.Vertices.Add(v)
            # copy over every face but flipped
            for f in mesh.Faces:
                new_mesh.Faces.AddFace(f.Flip())
            # in case of UV mapping, didn't test extensively
            for uvp in mesh.TextureCoordinates:
                new_mesh.TextureCoordinates.Add(uvp)
            # not sure if this is needed
            new_mesh.RebuildNormals()
            # replace 
            sc.doc.Objects.Replace(guid, new_mesh)

Testing here seems to work. Running multiple times on same ‘inverted’ mesh results in only trying to flip it once.

Catches are if you have really dense meshes, this is probably too slow. If you have imported normal maps or other fancy things associated with your meshes, it may not work as expected.