UDIMをバラバラにしたい

UDIMはとても便利で良い技術なんですが、たまにバラバラにしたくなるんですよね。

プレビューも最近のviewportとかだとできるんですけども、
やっぱりバラバラにしたくなるんですよね。


設計
--

バラバラと言ってもどうするかですが、最終的には1テクスチャー1マテリアルにして、
UVエリア毎にそのマテリアルをアサインするイメージですね。

なので、やらねばならない事は

- UDIMで使用されているテクスチャーを取得
- テクスチャー1枚ごとにマテリアルを作成、fileノードを接続しテクスチャを登録
- 該当するUVエリアのUVからfaceIDを取得し、マテリアルをアサイン

こんなですかね。

マスクとかテクスチャーが複数刺さってる場合もありますが、その辺は一旦見なかったことにして・・・

テスト用のデータをこんな感じで準備しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/f98fe059-f51b-3a96-80d0-b1a7e2f05ff0.png)


実装
--

- 該当するUVエリアのUVからfaceIDを取得

この部分を実装してみます。
まぁ例によって力技ですが、

- face総当たりでUVを取得
- UVポイントが指定した範囲に収まってるか否かを判定
- 判定がTrueならfaceのIDを格納

こんな流れでやってみます。

```python
import maya.api.OpenMaya as om

def findUVRangeFace(target,uvSet,UVRange):
    faceIds = []
    mesh_dagPath = om.MGlobal.getSelectionListByName(target).getDagPath(0)
    shape_Fn = om.MFnMesh(mesh_dagPath)
    face_ids = range(0,shape_Fn.numPolygons)
    it = om.MItMeshPolygon(mesh_dagPath)

    for index in face_ids:
        it.setIndex(index)

        try:
            uvs = it.getUVs(uvSet)
        except:
            continue
            
        insideRange = True
        for uPoint in uvs[0]:
            if uPoint < UVRange[0] or  uPoint > UVRange[1]:
                insideRange = False
                        
        for vPoint in uvs[1]:
            if vPoint < UVRange[2] or  vPoint > UVRange[3]:
                insideRange = False
                           
        if insideRange:
            faceIds.append(index)

    result = []
    for faceId in faceIds:
        result.append(target+".f["+str(faceId)+"]")
        
    return result

```

テストしてみますと、

```python
cmds.select(findUVRangeFace("pPlane1","map1",[0.0,1.0,0.0,1.0]))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/7fa71efe-73a6-b2d9-3373-d9b25e5f682e.png)


```python
cmds.select(findUVRangeFace("pPlane1","map1",[1.0,2.0,1.0,2.0]))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/d81d343f-8f57-b623-6342-17770d29c8b0.png)

うまいこと取れてそうですね。

これをベースに他も組んでみます。

```python
import maya.api.OpenMaya as om
import maya.cmds as cmds
import os
import pathlib
import re

def getUDIMFileIDs(fileNode):
    filePath = cmds.getAttr(fileNode + ".computedFileTextureNamePattern")

    fileDirPath = os.path.dirname(filePath) + "/"
    fileName = os.path.basename(filePath)    
    fileNameSample = fileName.replace("<UDIM>","([0-9|\-]{4})")    
    pathNode = pathlib.Path(fileDirPath).iterdir()    
    UDIMIDict = {}
    
    for node in pathNode:
        if node.is_dir():
            continue
                    
        elif node.is_file():
            ID = re.findall(fileNameSample,node.name) or None
            
            if ID == None:
                continue
            
            if ID[0] not in list(UDIMIDict.keys()):
                UDIMIDict[ID[0]] = fileDirPath + node.name

    return UDIMIDict

def findUVRangeFace(target,uvSet,UVRange):
    faceIds = []
    mesh_dagPath = om.MGlobal.getSelectionListByName(target).getDagPath(0)
    shape_Fn = om.MFnMesh(mesh_dagPath)
    face_ids = range(0,shape_Fn.numPolygons)
    it = om.MItMeshPolygon(mesh_dagPath)

    for index in face_ids:
        it.setIndex(index)

        try:
            uvs = it.getUVs(uvSet)
        except:
            continue
            
        insideRange = True
        for uPoint in uvs[0]:
            if uPoint < UVRange[0] or  uPoint > UVRange[1]:
                insideRange = False
                        
        for vPoint in uvs[1]:
            if vPoint < UVRange[2] or  vPoint > UVRange[3]:
                insideRange = False
                           
        if insideRange:
            faceIds.append(index)

    result = []
    for faceId in faceIds:
        result.append(target+".f["+str(faceId)+"]")
        
    return result


def separateUDIMMT(MTPrefix,fileNode,uvSet):

    UDIMFileDict = getUDIMFileIDs(fileNode)
    
    history = cmds.listHistory(fileNode,f=True) or []
    SGs = cmds.ls(history, type = "shadingEngine") or []

    if len(SGs) == 0:
        return

    ##generate UDIM MT
    UDIM_MTDict = {}
    for UDIMID in list(UDIMFileDict.keys()):
        filePath = UDIMFileDict[UDIMID]
        shader = cmds.shadingNode('lambert', name = MTPrefix + "_" +UDIMID, asShader=True)
        fileNode = cmds.shadingNode('file', asTexture=True)
        shadingEngine = cmds.sets(renderable=1, noSurfaceShader=1, empty=1, name=shader + "SG")            
        cmds.connectAttr(fileNode + ".outColor", shader + ".color")
        cmds.connectAttr(shader + ".outColor", shadingEngine + ".surfaceShader")
        cmds.setAttr(fileNode + ".fileTextureName",filePath,type = "string")
        UDIM_MTDict[UDIMID] = shadingEngine

    for SG in SGs:
        targets = cmds.sets(SG,q=True) or []

        for UDIMID in list(UDIM_MTDict.keys()):            
            UVRange = [
                        float(UDIMID[-1]) - 1.0,
                        float(UDIMID[-1]),                        
                        float(UDIMID[-2]),
                        float(UDIMID[-2])+ 1.0,
                        ]

            for target in targets:                
                faces = findUVRangeFace(target,uvSet,UVRange)
                if len(faces) != 0:
                    cmds.sets(faces,e =True,forceElement = UDIM_MTDict[UDIMID])

```

さてこれを実行してみると

```python
separateUDIMMT("hoge","file1","map1")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/bbeb414b-c7bf-b869-711f-1e6f699b9bca.png)

マテリアルが生成され、faceにアサインされました！