my funeral week

少しでも日々の生活に変化を。

MayaScripts_AddAnimatedAttrToAnimLayer : アニメーションレイヤーへのAttr追加補助

AnimationLayerを利用する際、アニメートされているAttributeだけをAnimationLayerに追加したいと思ったことはありますか?

AnimationLayerに対してノードを追加する際、いつも対象のノードを選択した状態でAnimationLayerEditor上のLayerを右クリックして表示されるコンテキストメニューから「選択したオブジェクトの追加」を実行しているのですが、この機能だとアニメート可能な全てのTransformAttributeとVisiblityがLayerに追加されてしまい・・・後にAnimationLayerをマージする際にLayerに追加された全てのAttributeがベイクされてしまいます。マージ時にスマートベイク等を利用すれば結果は変わりますが、AnimationLayerの利用目的にもよりますが、基本的にはベースのアニメーションに対して加算的にLayerを使いたいことの方が多いように思います。よって、そもそもアニメートされていないAttributeがLayerに追加される必要はないことが殆どではないでしょうか。

任意のAttributeだけをAnimationLayerに追加するには、チャンネルボックス上でAttributeを選択し、右クリックすると、コンテキストメニューに「選択したレイヤに追加」とありますので、これを実行すると良いのですが・・・対象のAnimationLayerを選択して、Attributeを選択して・・・と少々面倒に感じます。

なので、自動化するスクリプトを作りました。

AddAnimatedAttrToAnimLayer

# encoding:utf-8

import pymel.core as pm

VERSION = "0.2"

print ("\n-*- import {0}.{1} -*-".format(__name__,VERSION))

# 選択しているレイヤーがなかった場合の挙動
WITH_CREATE_NEWLAYER = True# 新規レイヤーの作成を行う
PRIORITY_CREATE_LAYER = False# 新規レイヤーの作成を優先する
PRIORITY_EXIST_LAYER = True# 既存レイヤーを優先する

SKIP_BASE_LAYER = True# Baseレイヤーを対象から除外する

ANIM_LAYER = ["animLayer"]# 対象とするAnimLayerノード
TARGET_NODE_TYPES = ["animCurve","animBlendNodeBase"]# Animatedと判断する接続ノードの種類

def __listAnimatableAttrsFromNode (target_nodes):
    """任意のノードから、AnimatableなAttrを返す"""
    # type:(list[pm.PyNode]) -> list[pm.general.Attribute]
    
    animatable_attrs = list()
    
    for n in target_nodes:
        animatable_attrs.extend(n.listAnimatable())
    
    return animatable_attrs


def __popAnimatedAttrs (target_attributes):
    """Attrのリストから、TARGET_NODE_TYPESに接続されているAttrだけを抽出"""
    # type:(list[pm.general.Attribute]) -> list[pm.general.Attribute]
    
    animated_attrs = list()
    
    for a in target_attributes:
        if a.connections(t = TARGET_NODE_TYPES, d = False):
            animated_attrs.append(a)
    
    print ("Animated attributes = {0}".format(animated_attrs))
    return animated_attrs

def __listAllExistAnimLayers ():
    """存在するAnimLayerを全て返す"""
    # type:() -> list[pm.nodetypes.AnimLayer]
    
    return [pm.nodetypes.AnimLayer(n) for n in pm.ls(type = ANIM_LAYER)]

def __getSelectedAnimLayersFromEditor ():
    """AnimLayerEditorで選択されているAnimLayerを返す"""
    # type:() -> list[pm.nodetypes.AnimLayer]
    
    selected_animlayers = list()
    base_layer = pm.animLayer(q = True, r = True)
    
    for al in __listAllExistAnimLayers():
        if SKIP_BASE_LAYER and al == base_layer : continue
        if pm.animLayer(al, q = True, sel = True):
            selected_animlayers.append(al)
    
    print ("Selected AnimLayers = {0}".format(selected_animlayers))
    return selected_animlayers

def __addAttrToAnimLayers (target_attrs, target_animlayers):
    """任意のAttrを任意のAnimLayerに追加する"""
    # type:(list[pm.general.Attribute],list[pm.nodetypes.AnimLayer]) -> None

    for al in target_animlayers:
        pm.animLayer(al, e = True, at = target_attrs)
    
    print ("Add Attrs:\n{0} \n\tto {1}".format([a.name() for a in target_attrs],target_animlayers))

def __createNewAnimLayer (name = "NewAnimLayer"):
    """新規AnimLayerを作成する(または同名のAnimLayerを返す)"""
    # type:(str) -> list[pm.nodetypes.AnimLayer]
        
    animLayer = None
    
    isAlreadyExist = pm.animLayer(name, q = True, ex = True)
    
    if isAlreadyExist and PRIORITY_CREATE_LAYER:
        animLayer = pm.animLayer(name + "_new")# 必ず新規Layerを作成
    elif not isAlreadyExist:
        animLayer = pm.animLayer(name)# 新規Layerを作成
    else:
        animLayer = pm.nodetypes.AnimLayer(name)# 既存の同名Layerを取得
    
    print ("Create or ExistAnimLayer = {0}".format(animLayer))
    return [animLayer]

def __findAnimLayerWithAttrs (target_attrs):
    """任意のAttrがIncludeされているAnimLayerを返す"""
    # type:(list[pm.general.Attribute]) -> list[pm.nodetypes.AnimLayer]

    found_animLayers = list()
    
    for al in __listAllExistAnimLayers():
        
        if al == pm.animLayer(q = True, r = True):continue
        
        layered_attrs = al.getAttributes()
        
        for a in target_attrs:
            if a in layered_attrs and al not in found_animLayers:
                found_animLayers.append(al)
    
    return found_animLayers

def __getValidAnimLayers(target_attrs):
    """有効なAnimLayerを返す"""
    # type:(list[pm.general.Attribute]) -> list[pm.nodetype.AnimLayer]
    
    animLayers = __getSelectedAnimLayersFromEditor()
    
    if animLayers:
        return animLayers
    
    if PRIORITY_EXIST_LAYER:
        animLayers = __findAnimLayerWithAttrs(target_attrs)
    
    if not animLayers and WITH_CREATE_NEWLAYER:
        animLayers = __createNewAnimLayer()
    
    return animLayers

def __addAnimatedAttrToLayer (target_nodes):
    """選択しているノードのAnimatedなAttrをAnimLayerへ追加する"""
    # type:(list[pm.PyNode]) -> None
    
    animatable_attrs = __listAnimatableAttrsFromNode(target_nodes)
    
    target_attrs = __popAnimatedAttrs(animatable_attrs)
    if not target_attrs : return
    
    target_animLayers = __getValidAnimLayers(target_attrs)
    if not target_animLayers : return
    
    __addAttrToAnimLayers(target_attrs, target_animLayers)

def main ():
    
    __addAnimatedAttrToLayer(pm.selected())

実行するには下記を参照。

import AddAnimatedAttrToAnimLayer
AddAnimatedAttrToAnimLayer.main()

実行すると、選択しているAnimationLayerに対して、シーン上で選択中のノードのアニメートされているAttributeだけが追加されます。 選択しているAnimationLayerがない場合は、新規でAnimationLayerを生成し、そのLayerを対象とします。

余談

ずっと疑問なんですけど、Mayaコマンドの「animLayer」の引数のひとつに「bestLayer」というものがありますけど、何をもってベストとするのか、イマイチ理解できていません。 公式のコマンドに「ベスト」という単語が使われているのも珍しくないっすか?そうでもない? しかし、ベストて・・・なんかのコマーシャル以外で聞かない単語ですよね。

もうひとつ疑問。 AnimationLayerEditorの各Layer名の右横にある、緑とか赤色のドットインジケーター。

それぞれ、なにを意味しているかは理解できているつもりなんですが、このインジケーターの正式名称て何? 公式のリファレンスには「アクティブなキーイング フィードバック」とか書いてあるけど、ではこれをコマンドから参照するには、なんという名前なの?って話。