Maya python script that converts triangulated mesh to quads from uv layout

Hi,

I’m writing a workflow script for Maya that attempts to convert a triangulated mesh into quads based on how the mesh UVs are laid out. I’m looking for feedback about 1) why the script is running so slow and 2) how to get the UV shell border as a selection of UVs through a python command.

The script assumes that the user is working with a specific kind of mesh, ie. strokes exported from the VR app Gravity Sketch.

On the mesh UVs, the central portion of the mesh is laid out in a grid-like fashion, as seen in the image below.

UVs

My thinking was that since the triangles can already be easily identified visually at a glance on the UVs, the conversion to quads shouldn’t be too difficult. I tried doing ‘Mesh > Quadrangulate’ on a variety of meshes, but I found that this command was not reliable in all cases. My guess is that ‘Mesh > Quadrangulate’ interprets the mesh in world space (not UV space, as I am trying to do).

My script goes like this: Iterate through each UV on the model. Check its neighbouring UVs as to whether they are lined up or not in UV space. If they are not lined up, consider the edge between as an edge causing triangulation, then delete the edge.

After running the script on the UV selection shown above, the edges causing triangles are deleted.

The problem is that the script runs super slowly. I’m not sure what’s going on here or if there is a way to speed this up.

I am also having a hard time getting the UV shell border through a command (or series of commands). I am wondering whether ‘Select > Convert Selection > To UV Shell Border’ exists as a command? I have not been able to find it.

Here is my script and a demo fbx file for testing. Thanks!

sFBXASC0460938.fbx


# the user selects a set of uvs and runs the script
# the script deletes the edges causing triangles

import maya.cmds as cmds
import math

# defines a value to not delete an edge based on whether the u or v values of two uvs in question are within this threshold
keepTol = 0.0001

# user selects a set of uvs
uvSelection = cmds.ls( selection=True, flatten=True )
# for all uvs in the selection
for uv in uvSelection :
    # set the first uv
    uv1 = uv
    # get the uv value
    uv1Val = cmds.polyEditUV( uv1, query=True )
    # get the adjacent edges
    adjEdges = cmds.polyListComponentConversion( uv1, toEdge=True )
    # get the adjacent uvs
    adjUvs = cmds.polyListComponentConversion( adjEdges, toUV=True )
    # select the uvs
    cmds.select( adjUvs )
    # remove the first uv from the selection
    cmds.select( uv1, deselect=True )
    # set the adjacent uvs
    adjUvs = cmds.ls( selection=True, flatten=True )
    # for the adjacent uvs
    for xy in adjUvs :
        # set the second uv
        uv2 = xy
        # get the uv values
        uv2Val = cmds.polyEditUV( uv2, query=True )
        # if the first and second uvs don't match within a given tolerance
        if abs( uv1Val[0] - uv2Val[0] ) > keepTol and abs( uv1Val[1] - uv2Val[1] ) > keepTol : # proceed to delete the edge between
            # get the vertices
            verts = cmds.polyListComponentConversion( uv1, uv2, toVertex=True )
            # get the edge
            edge = cmds.polyListComponentConversion( verts[0], verts[1], toEdge=True, internal=True )
            # delete the edge
            cmds.polyDelEdge( edge, cv=False )
            # exit the loop
            break

I was going to suggest OpenMaya API for faster speeds.

But the reason it is slow is because you’re running individual delete commands inside a loop, and the poly history is accumulating.

If you collect the edges in a list, “edgesToDelete”, and then delete all the edges in one single command, the script executes in 0.26 seconds instead of 734 seconds.

Using the OpenMaya API could potentially get it down drastically faster.

# the user selects a set of uvs and runs the script
# the script deletes the edges causing triangles

import maya.cmds as cmds
import math
import time

# start the timer
timeStart = time.clock()

# defines a value to not delete an edge based on whether the u or v values of two uvs in question are within this threshold
keepTol = 0.0001

# user selects a set of uvs
uvSelection = cmds.ls( selection=True, flatten=True )
# for all uvs in the selection
edgesToDelete = list()
for uv in uvSelection :
    # set the first uv
    uv1 = uv
    # get the uv value
    uv1Val = cmds.polyEditUV( uv1, query=True )
    # get the adjacent edges
    adjEdges = cmds.polyListComponentConversion( uv1, toEdge=True )
    # get the adjacent uvs
    adjUvs = cmds.polyListComponentConversion( adjEdges, toUV=True )
    # select the uvs
    cmds.select( adjUvs )
    # remove the first uv from the selection
    cmds.select( uv1, deselect=True )
    # set the adjacent uvs
    adjUvs = cmds.ls( selection=True, flatten=True )
    # for the adjacent uvs
    for xy in adjUvs :
        # set the second uv
        uv2 = xy
        # get the uv values
        uv2Val = cmds.polyEditUV( uv2, query=True )
        # if the first and second uvs don't match within a given tolerance
        if abs( uv1Val[0] - uv2Val[0] ) > keepTol and abs( uv1Val[1] - uv2Val[1] ) > keepTol : # proceed to delete the edge between
            # get the vertices
            verts = cmds.polyListComponentConversion( uv1, uv2, toVertex=True )
            # get the edge
            edge = cmds.polyListComponentConversion( verts[0], verts[1], toEdge=True, internal=True )
            edgesToDelete.extend(edge)
            # exit the loop
            break
cmds.polyDelEdge( edgesToDelete, cv=False )
# stop the timer
timeStop = time.clock()
print('Execution time: %s seconds.'%(timeStop-timeStart))

Maya has a built-in command to convert triangles back to Quads. It’s not 100% perfect, but it might get you pretty close.

Thank you @clesage ! This is great. Yeah I was wondering whether moving the delete outside of the loop would speed things up, but I never realized it would do so this much.

I see that you are storing the edges to a list variable. I haven’t used this kind of variable before so I am not totally sure about how it works. Where do I find info about python variable types? A quick Google search brought me here…
LearnPython.org
Although it’s not directly related to Maya. Can the info on this site be carried over to Python in Maya?

In your script when you declare the ‘edgesToDelete’ variable, could I achieve the same thing as you by doing this?

edgesToDelete = []

I have a related question (and perhaps a list variable could be a possible solution but I am not sure). I am wondering how to compare two selections of components (in my case, uvs) and remove one from the other. In the script, I attempt to do this in a somewhat clunky way (below) but maybe there is a more suitable approach using a set…

# get the adjacent uvs
adjUvs = cmds.polyListComponentConversion( adjEdges, toUV=True )
# select the uvs
cmds.select( adjUvs )
# remove the first uv from the selection
cmds.select( uv1, deselect=True )
# set the adjacent uvs
adjUvs = cmds.ls( selection=True, flatten=True )

Regarding OpenMaya API, I have never used it and I am finding it difficult to translate my existing knowledge of Maya Python commands to OpenMaya API. To get started, where can I find commands for OpenMaya API? Google brought me to this page from 2010…
Maya API Reference

myFancyList = list() and myFancyList = [] are equivalent. (edit: nearly equivalent. It seems there are some subtle differences. python - [] and {} vs list() and dict(), which is better? - Stack Overflow) Both make an empty list. You can also use dict() to make an empty dictionary, or set() to make an empty set.

I don’t have a decent answer for you about removing the original UVs. You can remove elements from a list. You might also use a set instead of a list. A set can only contain unique elements, so if there are times when you end up with a UV edge being adjacent to multiple elements, a set would prevent it from being added multiple times. And sets have methods you may be able to use to easily remove elements you don’t want. (unions and intersections, etc.)

OpenMaya API is a slog… It’s extremely fast to run, but it’s difficult to learn and write. Nothing is straight-forward. You can’t just run simple commands. You’ll need to learn to parse the documentation, find examples online, and perhaps read Practical Maya Programming. Just avoid it if you don’t need it. But if you are doing tools that need to be used many times, the optimization is well worth it.

Thanks @clesage ! This info regarding lists, sets and dictionaries is helpful. I was just exploring sets briefly and it seems like they are a good fit for what I’m trying to do with mesh components (like uvs) where there should only be one entry per component, and no duplicates.

And thank you for the info about OpenMaya API. Good to know!