Clean Poly Combine & mergeAndCull - better ways to merge polys than PolyUnite

Posted 8 October, 2012 Anthony Tan (staring at yet another render. But still loving it for some reason..)

« previous | next »

Just a pair of scripts, one to perform a much cleaner, saner merge than the PolyUnite command (which I found), and one to merge verts and bust duplicate faces (which I wrote).

Nothing too insightful today, just a link to a copy of a handy dandy bit of MEL script that can effectively replace the existing polyUnite command. Originally from http://www.mayzie.net/links/clean-poly-combine I take absolutely no credit whatsoever for it. But it's neat, and makes my life easier.

One thing the script doesn't do is vertmerging and busting duplicate faces however. This may not happen to you much but the specific workflow I'm thinking of here is from when I build 'bits' and vert-snap things together (for example, that teeth on a cog thing). You end up with a lot of overlapping/identical vertices and if you weren't careful, internal faces that need to be busted. So obviously, in the spirit of making life easier, script:

(requires you to have pymel imported as pm)

def mergeAndCull(selectOnly=False, tolerance=0):
    '''
    For the first selected object, merge all verticies closer than tolerance 
    default 0) and also delete all faces that share all verts.

    NOTE: will only process the first selected object, and the topmost item
    in a tree. You probably want to select the object directly.
    
    '''

    # You can have a transform selected (nt.Transform) or an nt.Mesh.
    # at this stage, we don't support selection of meshfaces..

    targetobj = pm.ls(sl=True)[0]
    originalSelect = targetobj
    # object got, remember selection for later.
    
    # walk down the tree to get to the lowest point where we have something
    # other than a transform node, picking the 0th object each time.
    while targetobj.nodeType() == u'transform' :
        targetobj = targetobj.getChildren()[0]
        
    # two traps - 1 : we must have a Mesh (nodeType() == mesh)
    #           - 2 : must not have the getChildren() function 
    # trap 2 picks up when we have a MeshFace selected
    try:
        targetobj.getChildren()
    except:
        raise TypeError("function doesn't yet work on MeshFaces, please select the object")
    
    if targetobj.nodeType() != u'mesh':
        raise TypeError("function requires a Mesh object")

    # at this point, we know we've got a Mesh to work with and need to get faces
    faces_to_process = targetobj.faces

    # convert selection to vertices, select them and then polymerge
    vertIndicies_to_process = []
    for x in faces_to_process:
        vertIndicies_to_process = vertIndicies_to_process + x.getVertices() # basic flattening

    # remove duplicates..
    vertIndicies_to_process = set(vertIndicies_to_process)

    # these are now all the vertex IDs sanely done
    # node() (part of Component) will return what node this belongs to
    # vtxs etc all subclass component
    # iterate and select each vertex


    # HACK WARNING.. the faces_to_rpocess[0] indexation SHOLD NOT WORK if you 
    # have multiple objects selected. Needs to be fixed up and rewritten to 
    # either gracefully manage this, OR, trap this in user input stage.
    vertices_to_process = list()
    for x in vertIndicies_to_process:
        vertices_to_process.append(faces_to_process[0].node().vtx[x])

    pm.polyMergeVertex(vertices_to_process, d = tolerance)


    # ALL faces sharing all verts to be blasted.
    # the only situation where this isn't a good idea is when you're pasting a single patch on top
    # of the exsting object, in which case you get a hole.
    # more likely with a combine/merge operation you're worried about internal faces..
    facelist=list()
    duplicatefaces = list()

    # first pass - extract duplicates. If we tried to do this in a single pass, there's
    # a bug where you've got odd number of faces (triple stacking or suchlike)
    # and you'd have to build the function to check if it's in the duplicatefaces or blah.
    # basically algo is:
    # is in facelist? No. Add to facelist. Yes? add to dupelist AND then pop from facelist.
    # is in dupelist? No. Don't care. Yes? Add to dupelist..

    for x in faces_to_process :
        if sorted(x.getVertices()) in map(lambda x: sorted(x.getVertices()), facelist):
            duplicatefaces.append(x)
        else:
            facelist.append(x)

    # second pass, go through the facelist and do a reverse check so we pick up any of the dupes
    # that went into a unique faces list, and we kill them. Kind of a harsh approach.
    for x in facelist:
        if sorted(x.getVertices()) in map(lambda x: sorted(x.getVertices()), duplicatefaces):
            duplicatefaces.append(x)

    if selectOnly :
        pm.select(duplicatefaces)
    else :
        if len(duplicatefaces) != 0:
            print ("faces deleted:")

            for x in duplicatefaces:
                print(x)
        else:
            print("No faces to delete")

        pm.delete(duplicatefaces)
        pm.select(originalSelect)
 
previous
Good Cylinder, Bad Cylidner
next
Maya gotchas - the lastPosition attribute of particles