MayaScripts_EasyBakeAnimations : アニメーションの焼き付け(ベイク)補助ツール
アニメーション制作時にもっとも多く行う面倒なことってなんでしょうか?
私にとってアニメーションのベイクがそれに当たります。
2022/11/29 Update
EasyBakeAnimations
Mayaのアニメーションのベイクってなんであんなに面倒なんでしょうか?
チャンネルボックスからいちいちベイクしたいアトリビュートを選択して、フレーム数を入力して・・・。
作業中に何度もこれを操作する時代はもう終わりです。
今日からは以下のコードを使ってください。
# encoding:utf-8 import pymel.core as pm VERSION = "1.3" print ("\n-*- import EasyBakeAnimations.{0} -*-".format(VERSION)) WINDOWNAME = "EasyBakeAnimations" WINDOWSIZE = [200,100] DEFAULT_CHANNELBOX = "mainChannelBox" PRESERVE_OUTSIDE_KEYS = True REMOVE_ATTR_FROM_LAYER = True TARGET_NODE_TYPES = [ "animCurve", "pairBlend", "pointConstraint", "orientConstraint", "scaleConstraint", "aimConstraint", "parentConstraint", "animBlendNodeBase" ] BAKE_TARGET_ATTRS = [ "translateX", "translateY", "translateZ", "rotateX", "rotateY", "rotateZ", "scaleX", "scaleY", "scaleZ" ] def __getAttrFromChannelBox (target_node, channelBox_name): """任意のノードとチャンネルボックスからアトリビュートを返す""" # type:(pm.PyNode, str) -> list[pm.general.Attribute] attributes = list() for a in pm.channelBox(channelBox_name, q = True, sma = True): if not pm.attributeQuery(a, n = target_node, ex = True) : continue attr = target_node.attr(a) if attr.isLocked() or not attr.isKeyable() : continue attributes.append(attr) return attributes def __getAnimatableAttrFromNode (target_node): """任意のノードからアニメーション可能なアトリビュートを返す""" # type:(pm.PyNode) -> list[pm.general.Attribute] animatable_attrs = list() for a in target_node.listAnimatable(): if a.attrName(longName = True) in BAKE_TARGET_ATTRS: animatable_attrs.append(a) return animatable_attrs def __popAnimatedAttrs (target_attributes): """アトリビュートリストからAnimatedなアトリビュートをPopする""" # type:(list[pm.general.Attribute]) -> list[pm.general.Attribute] animated_attrs = list() for a in target_attributes: # AttrにTARGET_NODE_TYPESの接続があればAnimatedとする。 if a.connections(t = TARGET_NODE_TYPES, d = False): animated_attrs.append(a) return animated_attrs def __bakeAnimations (target_attributes, framerange): """任意のアトリビュートのアニメーションをベイクする""" # type:(list[pm.general.Attribute, list[int]]) -> bool bake_result = False pm.refresh(su = True) try: pm.bakeResults( target_attributes, t = framerange, sm = True, pok = PRESERVE_OUTSIDE_KEYS, ral = REMOVE_ATTR_FROM_LAYER ) bake_result = True except: bake_result = False pm.refresh(su = False) return bake_result def __getCurrentFramerange (): """現在のframerangeを返す""" # type:() -> list[int] return [ pm.playbackOptions(q = True, min = True), pm.playbackOptions(q = True, max = True) ] def bakeTargetNodes (framerage = [], target_nodes = [], fromChannelBox = False): """任意のノードのアニメーションされているアトリビュートをベイクする""" # type:(list[int], list[pm.PyNode], bool) -> bool bake_attributes = list() target_nodes = target_nodes if target_nodes else pm.selected() for n in target_nodes: target_attrs = list()# type:list[pm.general.Attribute] if fromChannelBox: target_attrs = __getAttrFromChannelBox(n, DEFAULT_CHANNELBOX) else: animatable_attrs = __getAnimatableAttrFromNode(n) target_attrs = __popAnimatedAttrs(animatable_attrs) bake_attributes.extend(target_attrs) print ("\nAdd Bake Target : {0} {1}".format(n,[a.attrName() for a in target_attrs])) if not framerage or len(framerage) != 2: framerage = __getCurrentFramerange() bake_result = __bakeAnimations(bake_attributes, framerage) pm.headsUpMessage("---------- Complete Bake Animation : {0} ----------".format(bake_result)) return bake_result def __deleteExitsUI (): """既存のUIを削除""" # type:() -> None if pm.window(WINDOWNAME, q = True, ex = True): pm.deleteUI(WINDOWNAME) def __buildUI (): """framerange, channelbox flag を設定できるUIを表示する""" # type:() -> None __deleteExitsUI() with pm.window(WINDOWNAME, mnb = False, mxb = False) as w: w.setWidthHeight(WINDOWSIZE) with pm.columnLayout(adj = True, rs = 5): pm.text(l = "Bake Options", bgc = [0,0.1,0.1]) cf = pm.checkBox(l = "From Channelbox", v = False) with pm.rowLayout(nc = 3): currentrange = __getCurrentFramerange() sff = pm.floatField(v = currentrange[0], pre = 2, w = WINDOWSIZE[0]*0.4) pm.text(l = " - ", w = WINDOWSIZE[0]*0.15) eff = pm.floatField(v = currentrange[1], pre = 2, w = WINDOWSIZE[0]*0.4) command = lambda *arg : bakeTargetNodes([sff.getValue(), eff.getValue()], [], cf.getValue()) pm.button(l = "Start Bake", c = command) def main (): if pm.getModifiers() == 5: __buildUI() return bakeTargetNodes()
これで面倒なことが一個なくなりました。
概要
こいつのポイントは、アニメートされているアトリビュートを自動で判断でき、
ベイクするフレーム範囲もシーンの再生範囲が使われます。
つまり、ノードを選択してこいつを実行するだけでベイクは完了です。
なにも難しいことはありません。
使い方
このコードを適当なPythonファイルに保存して、
C:\Users\ユーザー名\Documents\maya\scripts
の中に配置して、
以下のようなコードをシェルフなりホットキーなりに登録するだけです。
import EasyBakeAnimations
EasyBakeAnimations.main()
一応、オプションとして簡単なUI表示もあります。
「Shift+Ctrl」を押しながら実行すると以下のような小さなウィンドウが表示されます。
レイアウトが若干乱れているのは気にしないでおくれ・・・。
オプション
From ChannelBox(チェックボックス)
数値入力フィールド
- ベイク範囲を設定する場所
- 左のフィールドが開始フレームで、右が終了フレーム
これ使うくらいなら、デフォルトのMayaのベイク機能を使うだろうから、
こいつのUI機能は多分誰も使わないと思うけど。一応ね。一応。
更新履歴
- ver 1.3 (22/11/29)
- Maya2022以降にて実行できるように修正。
- main関数からモディファイヤを使ってUIを表示できるように変更。
- ver 1.2 (21/05/01)
- アニメーションレイヤーがあってもベイクできるように修正。
以上。
おつした~。