「ポリゴンメッシュの凹面を自動判別する機能」があります。
早めに開発着手したかったのですが、なかなか手が空かず、
時間ばかりいただいてしまいました。。
で、ちょうど年末年始、数学に強い従兄の助言もいただき、
時間のあるこの時期に大体形にしてみたので、こちらでも紹介します。
■どうやって凹凸を判別するか?
チェックするポリゴンは4頂点の4角形であることが前提です。
そもそも3角形だったら平面だし。5角形だったらそもそもポリゴン割れし。
で、仕組みはこうです。
ずばり内積と外積を使います。
ベクトルの内積と外積を使って、4頂点のうち、
3頂点の平面から見た残りの1点の位置を判別します。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
かんたんに外積:

かんたんに内積:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
その手順は…、
①3頂点で作る、1点から伸びる2本のベクトルの外積を出して、
この3角形に垂直なベクトルを算出します。

②残り1頂点で作るベクトルと、①の三角形に垂直なベクトルの内積を求めます。

この内積の値が正か負かで、残りの1点の位置が算出できるはずです。
■しかし、ポリゴンのみかたで凹か凸かが変わってしまう。
この4頂点ですが…


こうみるかで、凹か凸かが変わってしまいます。
要は、どの頂点を判別するのか、準点を定める必要があるわけです。
ここは、判別の基準をアーティストさんに確認する必要がありますが、、、
とりあえず、一度Triangulateをかけた時の形状が、凹か凸かを判別するように
考えることにしました。
要は、このポリゴンを…

Triangulateをかけて…

この3角形に含まれない頂点…

つまり、この頂点を判別することにします。

■melに便利なコマンドが
melにズバリ、内積・外積を算出するコマンドがあるので、これを使うっきゃない。
外積:cross http://download.autodesk.com/global/docs/maya2014/ja_jp/Commands/cross.html
内積:dot http://download.autodesk.com/global/docs/maya2014/ja_jp/Commands/dot.html
しかし、頂点計算など、数が多い処理は全てmelで処理するには重たすぎるので、
基本はすべてpythonで書くことにし、pythonからmelプロシージャを呼び出すようにします。
そんな感じで、ぱっと作ってみました。
※使用方法は、mel文を先に実行しておくか、sourceコマンドで読んでおいた上で
ポリゴンを選択し、python文を実行します。
すると、凹んだ角形のみが選択された状態で終了します。
実際にまだ使っていないので、信頼性は低いですが。
いろいろテストなどして、ブラッシュアップしていこうと思います。
以下ソース
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//------------------------ | |
//mel部分 | |
//------------------------ | |
global proc float[] ReturnCrosProductList(vector $vec1,vector $vec2){ | |
vector $crossProc = cross($vec1,$vec2); | |
float $ReturnList[]; | |
$ReturnList[0]=$crossProc.x; | |
$ReturnList[1]=$crossProc.y; | |
$ReturnList[2]=$crossProc.z; | |
return $ReturnList; | |
} | |
global proc float ReturnDotProductList(vector $vec1,vector $vec2){ | |
float $ReturnFloat = dot($vec1,$vec2); | |
return $ReturnFloat; | |
} | |
//------------------------ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#python部分 | |
#------------------------- | |
# -*- coding: utf-8 -*- | |
import maya.cmds as mc | |
import maya.mel as mel | |
import math as math | |
def change_e_to_zero(List=[0,0,0]): | |
i=0 | |
for num in List: | |
numstr='%s'%num | |
if len(numstr.split('e'))>1: | |
List[i]=0.0 | |
i=i+1 | |
return List | |
def getRecessedFace(): | |
errorFaceList=[] | |
faceList=[] | |
trifaceList=[] | |
vtxList=[] | |
triVtxList=[] | |
chkVtxList=[] | |
P_org=[] | |
P_A=[] | |
P_B=[] | |
P_C=[] | |
vec1=[1,1,1] | |
vec2=[1,1,1] | |
vec3=[1,1,1] | |
vecNormal=[1,1,1] | |
vecCrossProc=[1,1,1] | |
moreFiveVtxFaceList=[] | |
faceList=mc.ls(mc.polyListComponentConversion(mc.ls(sl=True),tf=True),fl=True) | |
#mc.scriptEditorInfo(sr=False) | |
for face in faceList: | |
trifaceList[:]=[] | |
vtxList[:]=[] | |
chkVtxList[:]=[] | |
P_org[:]=[] | |
P_A[:]=[] | |
P_B[:]=[] | |
P_C[:]=[] | |
vtxList = mc.ls(mc.polyListComponentConversion(face,tv=True),fl=True) | |
try: | |
if len(vtxList)>4: | |
print 'This is more 5Vtx Face.' | |
elif len(vtxList)<4: | |
print 'This is Triangle Face.' | |
else: | |
#get target vtx | |
mc.select(face) | |
mc.polyTriangulate(face,ch=False) | |
trifaceList=mc.ls(sl=True,fl=True) | |
triVtxList=mc.ls(mc.polyListComponentConversion('%s'%trifaceList[0],tv=True),fl=True) | |
mc.undo() | |
mc.select(vtxList) | |
mc.select(triVtxList,d=True) | |
chkVtxList=mc.ls(sl=True,fl=True) | |
mc.select(cl=True) | |
#get vtx point | |
P_org=mc.pointPosition('%s'%triVtxList[0]) | |
P_A=mc.pointPosition('%s'%triVtxList[1]) | |
P_B=mc.pointPosition('%s'%triVtxList[2]) | |
P_C=mc.pointPosition('%s'%chkVtxList[0]) | |
#change e to 0 | |
P_org=change_e_to_zero(P_org) | |
P_A=change_e_to_zero(P_A) | |
P_B=change_e_to_zero(P_B) | |
P_C=change_e_to_zero(P_C) | |
#getVec | |
vec1 = [P_A[0]-P_org[0],P_A[1]-P_org[1],P_A[2]-P_org[2]] | |
vec2 = [P_B[0]-P_org[0],P_B[1]-P_org[1],P_B[2]-P_org[2]] | |
vec3 = [P_C[0]-P_org[0],P_C[1]-P_org[1],P_C[2]-P_org[2]] | |
vecStr1='<<'+str(vec1[0])+','+str(vec1[1])+','+str(vec1[2])+'>>' | |
vecStr2='<<'+str(vec2[0])+','+str(vec2[1])+','+str(vec2[2])+'>>' | |
vecStr3='<<'+str(vec3[0])+','+str(vec3[1])+','+str(vec3[2])+'>>' | |
vecCrossProc=mel.eval('ReturnCrosProductList('+vecStr1+','+vecStr2+');') | |
#get crossProduct Vec | |
vecCrossVecStr='<<'+str(vecCrossProc[0])+','+str(vecCrossProc[1])+','+str(vecCrossProc[2])+'>>' | |
#get dotProduct length | |
vecDotProc=mel.eval('ReturnDotProductList('+vecStr3+','+vecCrossVecStr+');') | |
if len(str(vecDotProc).split('e'))>1: | |
vecDotProc=0 | |
if vecDotProc>0: | |
errorFaceList.append(face) | |
except: | |
print ('skip ' + face) | |
#print errorFaceList | |
mc.select(errorFaceList) | |
getRecessedFace() |