motionPathノードをよく使うのですが、若干こまったことがありまして。
パラメトリック長(Parametric Length)
Maya においてカーブ沿いにオブジェクトが移動するときの配置方法を指定します。パラメトリック空間とパラメトリック長の 2 つの方法があります。パラメトリック長(Parametric Length)を選択すると、パラメトリック長方法がアクティブになり、選択解除すると、パラメトリック空間方法がアクティブになります。
パラメトリック空間方法では、カーブの U パラメトリック空間における位置がマーカーによって表されます。パラメトリック長方法では、カーブの全長に対する割合(%)で位置がマーカーによって表されます。パラメトリック長方法は、割合のモード方法ともいいます。これは、パス カーブの長さの割合に基づいてパスの評価が行われるためです。
2 つの方法の違いを理解するため、パラメータ設定は均一であっても CV の間隔が均等でないカーブを考えてみます。
このパラメーター、位置をそのままで切り替えたい時がたまにありまして。
ただその場合、uValueについても変換してあげないと位置が動いてしまいます。
調査
A:Parametric Length = True (fractionMode = False)
カーブの全長に対する割合(%)
B:Parametric Length = False (fractionMode = True)
カーブ上のパラメーター位置
※アトリビュートエディタ上の値と、実際のアトリビュートの値が逆なので混乱しますね。
B に関しては、nearestPointOnCurveノードなどで取れそうな気はします。
問題はAに関してですが、
全体の長さは出せますが、カーブの始点から現在位置までの長さをどうにか求められれば・・・
困ったのでAPIを眺めます。
こんなのがありました
OpenMaya.MFnNurbsCurve.findLengthFromParam()
まさにこれですね欲していたのは。
実装
import maya.cmds as cmds
import maya.api.OpenMaya as om
def getDagNode(target):
try:
sellist = om.MGlobal.getSelectionListByName(target)
return sellist.getDagPath(0)
except:
return None
def getShapeFn(dagPath):
FnShape = None
if dagPath.hasFn(om.MFn.kMesh):
FnShape = om.MFnMesh(dagPath)
elif dagPath.hasFn(om.MFn.kNurbsSurface):
FnShape = om.MFnNurbsSurface(dagPath)
elif dagPath.hasFn(om.MFn.kNurbsCurve):
FnShape = om.MFnNurbsCurve(dagPath)
return FnShape
def convertToParametricPoint(motionPath):
inputs = cmds.listConnections(motionPath + ".geometryPath",s =True,d =False) or []
if len(inputs) == 0:
return
curve = inputs[0]
dagPath = getDagNode(curve)
shapeFn = getShapeFn(dagPath)
curMode = cmds.getAttr(motionPath + ".fractionMode")
## getcur
curPosition = cmds.getAttr(motionPath + ".allCoordinates")[0]
curParam = shapeFn.getParamAtPoint(om.MPoint(curPosition))
curLengthParam = shapeFn.findLengthFromParam(curParam) / shapeFn.length()
if curLengthParam > 1.0:
curLengthParam = 1.0
if curMode:
cmds.setAttr(motionPath + ".fractionMode", 0)
cmds.setAttr(motionPath + ".uValue", curParam)
else:
cmds.setAttr(motionPath + ".fractionMode", 1)
cmds.setAttr(motionPath + ".uValue", curLengthParam)
cmds.refresh()
return
この状態で実行してみると
上手くいってそうですね。
もう一回実行してみると
元に戻りました!
何に使えるかはさておき、位置をキープしたまま切り替えができるようになりました。