Tiled UV Textures, Viewports and UV Editor

If you are working in a multi-tiled UV textures environment, Autodesk introduced a way to manage that kind of setup in Maya 2015 for file nodes. It is compatible with the Zbrush, Mudbox, Mari way of setting the tiles plus a custom one.

It’s great and all, you can now see all your textures in the UV editor but sadly… there are some limitations, such as you can only view the textures with the Viewport 2.0 and not the faster, less buggy Legacy one. As you may already now, the Viewport 2.0 is not really usable yet outside of playblasting, modelling and shading, so it kinda defeat the whole purpose of the implementation of the tiled UVs.

You can “display” your textures in the Legacy Viewport by setting the Textured channel to “Combined Texture” in the “Hardware Texturing” section of the shader, but the result is really really bad.

So it is either Viewport 1.0 and no UV Editor tiles, or slow buggy Viewport 2.0 and UV Editor tiles. And don’t you dare change the setting “Textured channel” with the UV texture editor opened or Maya will scream at you in pain!

// Error: file: /maya2015-x64/scripts/AETemplates/AEhardwareTextureTemplate.mel line 167: No object matches name: swatchShadingGroup //
// Error: file: /maya2015-x64/scripts/AETemplates/AEhardwareTextureTemplate.mel line 167: Object 'swatchShadingGroup' not found. //

Better go old school style then!

You can either import all your images in different files nodes and put them in a layered texture, but I find this node to be inconsistent and weird sometimes, the more solid implementation is to use a PlusMinusAverage node to concatenate all the different files. Ash Aiad already did a great step-by-step tutorial so I won’t talk about the specificities of this solution. Note that it is basically the same thing if you use the layered texture.

Everything will be ok in the viewport but sadly you won’t be able to see all your textures in the UV Editor nor the Hypershade…

Mix the old with the new.

One way to have all the advantages would be to use the old school solution, and override the display in the UV Texture Editor like the file node does in multiple tile mode. If you fiddled with the new file node and its new tiles options, you may have noticed the use of the new module maya.app.general.fileTexturePathResolver. Its purpose is to detect automatically from a single file all of its linked tiles depending of the tile mode, frames, etc. so let’s use that to our advantage!

Unfortunately I haven’t been able to find a way to override the display in the UV Editor and Hypershade, and Autodesk haven’t put anything about that in the MEL command reference yet. So if anyone has any information regarding that, I’d be glad to hear it!

Here is a functional script doing all the node and right connexions automatically. You just have to connect the PlusMinusAverage to the desired channel. Feel free to use it and to change it.

import maya.cmds as cmds
import maya.app.general.fileTexturePathResolver as ftpr

def createFileNode():
    texture = cmds.shadingNode('file', asTexture=True)
    placement = cmds.shadingNode('place2dTexture', asUtility=True)
    attributes = ['.coverage', '.translateFrame', '.rotateFrame', '.mirrorU', '.mirrorV', '.stagger', '.wrapU', '.wrapV', '.repeatUV', '.offset', '.rotateUV', '.noiseUV', '.vertexUvOne', '.vertexUvTwo', '.vertexUvThree', '.vertexCameraOne']
    for attr in attributes:
        cmds.connectAttr(placement + attr, texture + attr, force=True)
    cmds.connectAttr(placement + '.outUV', texture + '.uv', force=True)
    cmds.connectAttr(placement + '.outUvFilterSize', texture + '.uvFilterSize', force=True)
    return texture, placement

imagePath= '/path_to_your/file.1001.jpg'
useFrame = False
frameNumber = None
uvTilingMode = 3
# 1 = 0-based (ZBrush)
# 2 = 1-based (Mudbox)
# 3 = UDIM (Mari)

pattern = ftpr.getFilePatternString(imagePath, useFrame, uvTilingMode)
allFiles = ftpr.findAllFilesForPattern(pattern, frameNumber) # The list of files detected following the file passed initially
uvcoords = ftpr.computeUVForFiles(allFiles, pattern) # To get the Maya UVs coordinates of the files
uvcoords = [uvcoords[i:i+2] for i in xrange(0, len(uvcoords)-1, 2)] # The default output being awfull, refactor it to be a real list of list of uv coordinates

plusminus = cmds.shadingNode('plusMinusAverage', asUtility=True)
for index, path in enumerate(allFiles):
    fil, placement = createFileNode()
    cmds.setAttr(fil + '.defaultColor', 0, 0, 0, type='double3')
    cmds.setAttr(fil + '.fileTextureName', path, type='string')
    cmds.setAttr(placement + '.wrapU', 0)
    cmds.setAttr(placement + '.wrapV', 0)
    cmds.setAttr(placement + '.translateFrameU', uvcoords[index][0])
    cmds.setAttr(placement + '.translateFrameV', uvcoords[index][1])
    cmds.connectAttr('{}.outColor'.format(fil), '{}.input3D[{}]'.format(plusminus, index))