nurbsCurveを使って zipperの様な構造を作りたいなぁと。
片方の端から順番にくっついていくイメージで。
CVが指定の位置に移動してくれればいいのだけども、さてCVの位置をどのように移動させたものか
案だし
-
cvPosition にコネクト
割とお手軽な気もするんだけども、スキニングできなくなってしまう。
zipperするカーブ -> スキニングするカーブ ってヒストリ繋げちゃえば大丈夫かも? -
blendShape
形状とか考えると手間はすくなそう。
blendShape後にスキニングも可能。
ただし一斉に移動してしまう。CV毎のウェイトを操作すればいけるかも?
blendShapeでやってみる
今回はblendShapeでやってみようかと思います。
赤いカーブを青いカーブにフィットさせてみます。
まずは 青カーブをターゲットとしてblendShapeを適用。
それぞれのCVが対応するCVに対してリニアに移動していきます。
ただ、今回はこれは理想的ではないので各コンポーネントのウェイトを見てみます。
全体のweightに対して、各コンポーネントのウェイトが乗算される感じですね。
さて、このアトリビュートの本体はどこにあるか・・・
色々いじってみて発見。 targetWeights
このattrに対して順番に数値を入れて行けばzipperできる はず。
スクリプト化
- 全体のパラメーターは 0 ~ 1.0
- CVのindex は 0 ~ 順番に処理(今回両端は元からくっ付いてるけども無視)
まずは blendShapeの適用
import maya.cmds as cmds
def curveZipper(startCurve,targetCurve):
cmds.blendShape(targetCurve,startCurve,parallel =False, o = "local",name = startCurve + "_BS", w = [(0,1)])
ウェイトは各コンポーネントのウェイトで制御するので、ターゲットウェイトは最初から1に設定
blendShapeNodeのtargetWeightsを順番に処理したいので、
cvのindexを取得。今回はかなり適当に取得
import maya.cmds as cmds
def curveZipper(startCurve,targetCurve):
BSNode = cmds.blendShape(targetCurve,startCurve,parallel =False, o = "local",name = startCurve + "_BS", w = [(0,1)])[0]
CVs = cmds.ls(startCurve + ".cv[*]",fl =True)
パラメーターを設置するノードを指定し、主パラメーターを追加。
import maya.cmds as cmds
def curveZipper(startCurve,targetCurve,paramCtrl):
BSNode = cmds.blendShape(targetCurve,startCurve,parallel =False, o = "local",name = startCurve + "_BS", w = [(0,1)])[0]
CVs = cmds.ls(startCurve + ".cv[*]",fl =True)
cmds.addAttr(paramCtrl, ln = "zipper",at = "double", dv = 0.0, min = 0.0, max = 1.0, k = True)
0.0~1.0 を cvの数で分割し、各範囲の数値をsetRangeをつかって0.0~1.0 に変換。
その結果を各targetWeightにコネクト
という流れ
import maya.cmds as cmds
def curveZipper(startCurve,targetCurve,paramCtrl):
BSNode = cmds.blendShape(targetCurve,startCurve,parallel =False, o = "local",name = startCurve + "_BS", w = [(0,1)])[0]
CVs = cmds.ls(startCurve + ".cv[*]",fl =True)
cmds.addAttr(paramCtrl, ln = "zipper",at = "double", dv = 0.0, min = 0.0, max = 1.0, k = True)
paramDif = 1.0 / len(CVs)
for i in range(0,len(CVs)):
setRange = cmds.createNode("setRange")
startParam = paramDif * i
endParam = paramDif * (i+1)
cmds.connectAttr(paramCtrl + ".zipper", setRange + ".valueX")
cmds.setAttr(setRange + ".oldMinX",startParam)
cmds.setAttr(setRange + ".oldMaxX",endParam)
cmds.setAttr(setRange + ".minX",0.0)
cmds.setAttr(setRange + ".maxX",1.0)
cmds.connectAttr(setRange + ".outValueX",BSNode + ".inputTarget[0].inputTargetGroup[0].targetWeights["+str(i)+"]")
curveZipper("curve1","curve2","null1")
ひとまず出来ました。
課題
- 元からくっ付いてる 最初と最後は処理から除外してもいいかもしれない
- CVの位置が均等ではないので、0.0~1.0を均等に分割すると移動速度に差が出てしまう。
- 0~-1.0で反対向きからとかもできるかもしれない。
- 1CVに1つsetRangeが作られるのがなんか無駄感ある。