mayaがエラー・ワーニングを吐く時の対処 9　~non-Manifoldとの戦い~

non-manifold ですって

発端はこれ

```
// Warning: The OpenSubdiv Catmull-Clark subdivision method cannot be applied to non-manifold geometry
```

smoothPreviewをonにするとこんなワーニングが出てくるデータに遭遇。
スクリプトエディタにはopen/import時にこのワーニング出てるんですけど、
思った以上にスクリプトエディタってのは見られてないらしい。

ワーニングの根源
-
今回ジオメトリはこんな感じになってました。
smoothPreviewをoffにすると一見問題ないのですが、
実は赤丸の部分にはエッジが2本重なっておりマージされてません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/dd354987-b01c-7a71-48c4-a95d0cbc93e4.png)

目視でこれを見つけるのはかなーり大変な気がしますね。


そもそも non-manifold
-
ところで non-manifoldって？
のん　まにふぉーるど？

https://help.autodesk.com/view/MAYAUL/2016/JPN/?guid=GUID-8E97CEF7-1CFE-4838-B4B7-59F526E21AB2

なるほど？
雑に言えば、無茶なつながり方をしてるポリゴンか。
今回の場合は　**2つ以上のフェースが1つの頂点を共有する場合(エッジは非共有)**　かなぁ

どうやって見つける？
-

- とりあえずmaya標準の機能でやってみる。
cleanupでできるらしい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/ff8b70fd-e692-a592-0462-b4aacd8a2b2b.png)

ビックリするぐらい引っかからない！
設定が悪い気もするけれども、一回忘れよう。

えーじゃぁどうしよう、力技でvtx数とかポリゴン数とかでなんとかするー？
ってtwitterでモゴモゴ言ってたら

https://twitter.com/IshikoTeruyuki/status/1670638153752010754?s=20

polyInfo ってコマンドがあるそうですわ！

https://help.autodesk.com/cloudhelp/2022/JPN/Maya-Tech-Docs/CommandsPython/polyInfo.html

なるほど、non-manifoldの為のオプションが幾つかありますね。

※invalidVertices　これ使えば前の記事の様に面倒なことしなくても孤立したvtx見つけられそうですね

polyInfoを試す
-

こんなメッシュの場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/454ca1ab-cefe-61ca-2a7c-02b3cd00fa97.png)

これで
```
print(cmds.polyInfo("pPlane6",nonManifoldEdges =True))
print(cmds.polyInfo("pPlane6",nonManifoldVertices =True))
```

こう
```
['pPlane6.e[1]', 'pPlane6.e[12]', 'pPlane6.e[23]', 'pPlane6.e[34]', 'pPlane6.e[45]', 'pPlane6.e[56]', 'pPlane6.e[67]', 'pPlane6.e[78]', 'pPlane6.e[89]', 'pPlane6.e[100]']
['pPlane6.vtx[0]', 'pPlane6.vtx[6]', 'pPlane6.vtx[12]', 'pPlane6.vtx[18]', 'pPlane6.vtx[24]', 'pPlane6.vtx[30]', 'pPlane6.vtx[36]', 'pPlane6.vtx[42]', 'pPlane6.vtx[48]', 'pPlane6.vtx[54]', 'pPlane6.vtx[60]']
```

お、見つけられそうですね。

ではこいつの場合
※わかりづらいのでsmoothPreview = On

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/65fb08fa-36b8-d8fc-058c-9f1f22b54e6e.png)

さぁ
```
print(cmds.polyInfo("pPlane3",nonManifoldEdges =True))
print(cmds.polyInfo("pPlane3",nonManifoldVertices =True))
```

えー・・・・・・・
```
None
None
```

力に頼ろう
-

要は、・・・・・どうすればいいんだ？

- vtxを完全に共有しているedgeを探す

かな？
とりあえずAPI2のドキュメントを眺めてみて

OpenMaya.MItMeshPolygon.getConnectedEdges()
OpenMaya.MItMeshPolygon.getConnectedFaces()
OpenMaya.MItMeshPolygon.getConnectedVertices()

ポリゴンだけどもこの辺が使えそう。

**getConnectedEdgesを試してみる**

```
import maya.api.OpenMaya as om
target = "pPlane3"
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)
it.setIndex(130)
edges = it.getConnectedEdges()

for edge in edges:
    cmds.select(target + ".e["+str(edge)+"]",add =True)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/19a697a2-6041-c03d-3873-37c2313f24cb.png)
お、重なってるうちの1つのエッジが取れましたね。


**getConnectedVerticesを試してみる**
```
import maya.api.OpenMaya as om

target = "pPlane3"
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)
it.setIndex(130)
vtxes = it.getConnectedVertices()

for vtx in vtxes:
    cmds.select(target + ".vtx["+str(vtx)+"]",add =True)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/a0c13208-2b88-36d9-9c2c-aa3c267b7396.png)

重なってるエッジが接続されてるvtxが取れましたね。

問題ない場合はこうなるはず
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/fda72953-d710-5a3d-9399-3f37c5dc084d.png)

てことは
**getConnectedVerticesで取得した接続されているvtx**
と
**faceを構成しているvtx**
を比較して、被ってる場合は non-manifold　ってことでいいのかしら？

実装
-
じゃぁまぁそういう事で一回まとめると


```python
def listSlitVtx(target):
    vtxIds = []
    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)

        #faceを構成するvtxIDを取得
        vtxs = it.getVertices()

        #faceに接続されているvtxIDを取得
        connectedVtx = it.getConnectedVertices()

        #2種類のvtxIDの積集合を取る
        product = list(set(vtxs) & set(connectedVtx))
        if len(product):
            vtxIds = list(set(vtxIds) | set(product))
              
    result = [target + '.vtx[' + str(vid) + ']'for vid in vtxIds]
    return result

cmds.select(listSlitVtx("pPlane3"),add=True)

```
なんか　listSlitVtxは違う気がするけどもまぁ仮という事で。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/d85a319e-d898-66db-0590-9c0d0b21de9f.png)

お　取れました。

こっちのタイプだと無反応なので、こっちはpolyInfoでひっかけた方が良さそうですね
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/02d478f7-0ede-e535-9c14-8759c69c22dd.png)

ひとまずこれで。
まぁ完全ではないけども、目視で探すよりは100倍くらい楽ですかね。