How does Maya determine the primary UV set and the UV set order?

An acquaintance of mine complained that when exporting stuff from Maya into his in-house game engine, the active UV-set in Maya is not always the set that becomes texCoords0 in the engine. Clicking another UV set in the UV set editor doesn´t seem reliable, as sometimes another set becomes texCoords0 in the engine.

So my questions are: How do I check what´s really the primary (active) UV set of a mesh, and how do I see the order of UV sets?

I think it’s only possible through script:

getAttr pCube1.uvSet[0].uvSetName

Afaik UV-Set number 0 is the primary set, regardless of where it shows up in the UV Set Editor.

[QUOTE=saschaherfort;25819]I think it’s only possible through script:

getAttr pCube1.uvSet[0].uvSetName

Afaik UV-Set number 0 is the primary set, regardless of where it shows up in the UV Set Editor.[/QUOTE]

Thanks. Is that array ordered? (ie pCube1.uvSet[1].uvSetName is the second set, pCube1.uvSet[2].uvSetName the third and so on)

Doesn’t look like it.

For example I was able to create 2 new UV Sets, delete the second set and still query set 0, 1 and 2 via script. The only difference was that uvSet[1] after deletion no longer had a name, but still showed up in the node-editor.
So you can have zombie-UV-sets that won’t show up in the UV-set editor, but still exist as an attribute on the node.

I can second these findings. It’s pretty ugly. You would think that Maya would garbage collect (compact) UV sets, but it doesn’t. You can have 6 UV sets, delete the middle four, and your UV sets will be at indices 0 and 5 with 4 empty sets in between. I can’t recall if they contain null data or zeroes. If you want’ to clean this up, you have to manually copy and delete the data, and Maya will only clean up the collection/array after a save and reload.

UV Set 0 cannot be deleted, you are always guaranteed at least one UV Set. The array is ordered, index [0] is always the base UV Set.
To clear out zombie UV sets you need to re-load the Maya file. Un-used Indices are not saved, so the only way to clear them is to save and reload the file.

From the Maya docs:

“Once a value has been added to an array plug, it will usually remain in the datablock as long as the scene is open. The exception is for attributes that have their disconnect behavior set to kDelete: data for these attributes will be deleted when their connection is broken. When a file is saved, only array values that are not at their default values will be written out. Therefore, the number of elements in an array plug may change when the file is read in again.”

http://help.autodesk.com/view/MAYAUL/2015/ENU/?guid=__cpp_ref_class_m_plug_html

1 Like

If you want to see the UV Set Names and their corresponding Index, you can do this:


import maya.cmds as cmds
import re

def getAllUvSets(shape):
    uvAttrs = cmds.listAttr(shape, multi=True, st="uvSetName")
    uvDict = {}
    for attr in uvAttrs:
        uvSetName = cmds.getAttr('{0}.{1}'.format(shape, attr))
        uvSetIndex = re.compile('(?<=\[)[0-9]*(?=\])').search(attr)
        uvDict[str(uvSetName)] = int(uvSetIndex.group(0))
    return uvDict



Also, the documentation for the polyUVSet command has some relevant examples:

http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/CommandsPython/index.html


# start with poly object
cmds.polySphere(n='plg', cuv=1, r=10.0)
...
# To query the logical indices of the uv sets in the
# uv set array of the node
node = "plg"
indices = cmds.polyUVSet(node, query=True, allUVSetsIndices=True)
for i in indices[:]:
	name = cmds.getAttr(node+".uvSet["+str(i)+"].uvSetName")
	print("Found uv set called " + name )

[QUOTE=btribble;25822]I can second these findings. It’s pretty ugly. You would think that Maya would garbage collect (compact) UV sets, but it doesn’t. You can have 6 UV sets, delete the middle four, and your UV sets will be at indices 0 and 5 with 4 empty sets in between. I can’t recall if they contain null data or zeroes. If you want’ to clean this up, you have to manually copy and delete the data, and Maya will only clean up the collection/array after a save and reload.[/QUOTE]

Maya would have to maintain any incoming or outgoing connections if it were to dynamically re-order indices to fill empty slots… would be messy either way…

[QUOTE=rgkovach123;25826]Maya would have to maintain any incoming or outgoing connections if it were to dynamically re-order indices to fill empty slots… would be messy either way…[/QUOTE]
Agreed. It would be nice if they had some sort of CompactUVs command. The biggest issue is when exporting and importing to/from other formats.

some utilities that might be useful


'''
mayaScene.uv

namespace for uv manipulation and queries
'''

import maya.cmds as cmds


def get_names(obj):
    '''
    returns a name:index dictionary of the uvsets on the supplied object
    '''
    uvindices = cmds.polyUVSet(obj, q=True, uvn=True)
    uvnames = cmds.polyUVSet(obj, q=True, auv=True)
    return dict(zip(uvnames, uvindices))


def get_indices(obj):
    '''
    returns am index:name dictionary of the uvsets on the supplied object
    '''
    uvindices = cmds.polyUVSet(obj, q=True, uvn=True) or []
    uvnames = cmds.polyUVSet(obj, q=True, auv=True) or []
    return dict(zip(uvindices, uvnames))


def collapse(obj, map=None):
    '''
    Deletes all UV channes except the first. If the optional map parameter is supplied, that map will be copied into 
    the first map channe;

    Returns a string list of the deleted channel names
    '''

    indices = get_indices(obj)
    if map:
        cmds.polyUVSet(obj, cp=True, uvs=map, nuv=indices[0])
    else:
        cur_map = cmds.polyUVSet(obj, q=True, cuv=True)[0]
        if cur_map != indices[0]:
            cmds.polyUVSet(obj, cp=True, uvs=cur_map, nuv=indices[0])
    index_list = [k for k in indices]
    index_list.sort()
    index_list.reverse()
    index_list.pop()
    delenda = []
    for item in index_list:
        cmds.polyUVSet(obj, delete=True, uvs=indices[item])
        delenda.append(indices[item])
    return delenda


def delete_empty_uvs(obj):
    '''
    Deletes all empty UV channels on the supplied object

    @note: the first UV channel is not touched, even it if is empty, since it cannot be deleted.
    '''
    indices = get_indices(obj)
    index_list = [k for k in indices]
    index_list.sort()
    index_list.reverse()
    index_list.pop()
    delenda = []
    for item in index_list:
        ct = cmds.polyEvaluate(obj, uv=True, uvs=indices[item])
        if ct == 0:
            cmds.polyUVSet(obj, delete=True, uvs=indices[item])
            delenda.append(indices[item])
    return delenda


def find_multiple_uv_sets():
    '''
    Returns a list of all the meshes in the scene with multiple UV sets
    '''
    lvi = lambda uv_obj: len(cmds.polyUVSet(uv_obj, q=True, auv=True)) > 1
    meshes = cmds.ls(type='mesh', ni=True)
    return filter(lvi, meshes)


def force_map_1(obj, map=None):
    '''
    collapse UV sets and make sure that the surviving UV channel is named 'map1' for finicky programs that care about 
    UV set names
    '''
    delete_empty_uvs(obj)
    collapse(obj, map)
    cmds.polyUVSet(obj, rename=True, newUVSet="map_rename_temp")
    cmds.polyUVSet(obj, rename=True, newUVSet="map1")


3 Likes

Minor necro post here.
I forgot to thank you Theodox for your awesome functions - they will definetly come in use. Thanks!!

The way Maya is dealing with UV sets are indeed funky. Renaming a set to map1 does not make it the primary set (as Maya see it) because the set on slot 0 in the array does not switch place with the set that the user is trying to make primary by renaming. But, if the user then exports the mesh (to obj or FBX), does the exporter look at the array or at the names (strings) of the UV set? A tools coder/TA would most likely just go with the names here in order to not confuse the artists (I would) - but what about those small studios who don´t have their own exporters, those who have to rely on the native OBJ/FBX exporter in Maya?
I´ve talked to a 3D artist at DICE about this and he told me they´ve been having some problems with this situation. The 3D Artists exports a mesh thinking that map1 is texCoords0 when in fact Maya thinks that whatever set which is on position 0 in the array is the correct one.

Also, say that a minor studio without their own export tools would have to deal with this: how would you modify the order of that array? Say that I want a minor tool for simply changing the UV set order (move up, move down, move to top etc). As I see it, if you want set X to be the -real- primary one (ie: its on slot 0) the only way is to create temporary UV sets and start copying sets around - or is there some easier way of doing it?

renaming UV Sets should absolutely not re-order the indices. UV Set names are only for display purposes only - the true identity of a uv set is its index.

your exporter should take into consideration the model and material together to determine which uv sets to export. in our export pipeline, the index of the uv sets do not matter, the model and material work out which uv set goes to which texture and exporter gets the data.

in our case, if two models share the same material, all the matters is that the names match on the two models - it doesn’t matter if map2 is index 1 on one model and index 2 on the other.

If you need to re-order the uv sets, and clean out empty indices, it will involve a lot of closing and re-opening of files so Maya will let go of the previously used index.

[QUOTE=rgkovach123;26641]renaming UV Sets should absolutely not re-order the indices. UV Set names are only for display purposes only - the true identity of a uv set is its index.

your exporter should take into consideration the model and material together to determine which uv sets to export. in our export pipeline, the index of the uv sets do not matter, the model and material work out which uv set goes to which texture and exporter gets the data.

in our case, if two models share the same material, all the matters is that the names match on the two models - it doesn’t matter if map2 is index 1 on one model and index 2 on the other.

If you need to re-order the uv sets, and clean out empty indices, it will involve a lot of closing and re-opening of files so Maya will let go of the previously used index.[/QUOTE]

Yea I would need to reopen a scene in order to clear out the zombie sets. But do I really need to do that for re-ordering? Can´t I just go with the workaround I proposed, creating temporary UV sets and using polyCopyUV()?

if you need the uv sets in a particular order, then yes, you will need to create temporary sets and copy/paste UVs.

there is another side-effect that can bite you - if you try to re-use the name of a deleted UV set, maya will start throwing fits, because it won’t know if you are referring the new set or the zombie set. After deleting a UV set, just re-open the file to clear references to it and allow the re-use of that uv set name.

In general, try to avoid reliance on invisible indices for anything important. I once worked with an engine that used outliner order for all sorts of things, even though 90% of maya artists don’t actually know you can reorder things in the outliner (and the other 10% do it for their own reasons, not the pipelines!)

it’s not hard to make sure that named sets exist, and those can be inspected by artists in the UV editor; the main problems with names are (as @rg points out) zombie names and typos. And the tendency of maya to do unpredictable things when merging meshes. So make sure to have a good bulletproofing script that checks for what your exporter needs and then run it before exporting – a user-friendly “you need to have 2 uvs named A and B” dialog from you is way better than a crashed exporter or gibberish in the game.