Search code examples
pythongeometrymaya-api

Is there a way to represent geometric object as a value in maya programmatically?


I am trying to group similar objects (models with transforms) in a maya project by their vertices programmatically and wanted to know if there was a value I could compute to check for similar objects ? The models I am working with is furniture from 3D houses. Example furniture

I want to group the teacups to a single group. Their names are completely different in the hierarchy, so I thought grouping them (creating a dictionary) by some geometrical similarity would help.


Solution

  • Comparing two meshes to detect similarities is a non-trivial problem and AFAIK Maya does not provide shortcuts for this. Also, I second Prune in saying that it is very important for you to be clear about what you mean by "similar objects": depending on the degree of similarity you aim to, you might need to implement quite complex (and/or very expensive) algorithms.

    By looking at the picture you linked, though, it seems to me that you just need to detect exact duplicates of the shape nodes, which is generally easier. Here's an example of how you can achieve that:

    from maya import cmds
    
    def getGeoHash(mesh):
        mesh_data = cmds.polyEvaluate(mesh)
        # simple dictionary hashing, as suggested here:
        # https://stackoverflow.com/questions/5884066/hashing-a-dictionary
        return hash(frozenset(mesh_data.items()))
    
    def groupSimilarMeshes():
        meshes = cmds.ls(typ='mesh', l=1)
        mesh_dict = {}
        for mesh in meshes:
            geo_hash = getGeoHash(mesh)
            if not geo_hash in mesh_dict:
                mesh_dict[geo_hash] = []
            # store the mesh transforms: there will be more than one
            # in case some meshes are instanced
            transforms = cmds.listRelatives(mesh, p=1)
            mesh_dict[geo_hash].extend(transforms)
    
        for key, duplicates in mesh_dict.iteritems():
            if len(duplicates) < 2:
                # non-duplicates are skipped
                continue
            cmds.group(duplicates, n='similar')
    
    groupSimilarMeshes()
    

    A few notes:

    • getGeoHash is the function which hashes your geometry; shapes (maya nodes of type mesh) are used, to ignore transformations
    • basic data returned by polyEvaluate is used for hashing, to keep things simple and computationally inexpensive
    • at some point you might need to refine your logic, as new exceptions emerge while using the script on different scenarios. To do that, you can add more data to your mesh_data dictionary, before hashing (ie. the bounding box size, both in 3D and UV space, the centroid coordinates, the presence of non-manifold geometry, etc.). An interesting thread with a few hints on this topic (though quite old) can be read here.