Skip to content

Instantly share code, notes, and snippets.

@yamahigashi
Created December 30, 2018 14:48
Show Gist options
  • Save yamahigashi/3eba581df545d861088796c32e4960c4 to your computer and use it in GitHub Desktop.
Save yamahigashi/3eba581df545d861088796c32e4960c4 to your computer and use it in GitHub Desktop.
# -*- coding: UTF-8 -*-
# ======================================================================
import os
from maya import cmds
from maya import mel
if False:
# For type annotation
from typing import Dict, List, Tuple, Pattern, Callable, Any, Text # NOQA
# ======================================================================
DEFAULT_TRACK_CONTAINER_NAME = "Composition1"
# ======================================================================
def create_composition_track(track_container_name):
# type: (Text) -> Text
"""Get or create track composition container."""
tracks = cmds.timeEditorComposition(q=True, allCompositions=True) or []
if track_container_name in tracks:
return track_container_name
return cmds.timeEditorComposition(track_container_name, createTrack=True)
def create_clip(track_path, nodes, clip_name):
# type: (Text, List[Text], Text) -> Tuple[Int, Int]
kwargs = {
"type": [
"animCurveTL",
"animCurveTA",
"animCurveTT",
"animCurveTU"
],
"addRelatedKG": True,
"recursively": True,
"includeRoot": True,
"rsa": 1,
"rootClipId": 1,
"aso": True,
"track": track_path
}
cmds.select(nodes)
clip_index = cmds.timeEditorClip(clip_name, **kwargs)
track_index = cmds.timeEditorClip(clip_index, q=True, track=True)
track_index = int(track_index.split(":")[-1])
return track_index, clip_index
def remap_clip(track_container_name, track_index, clip_index, remap_to):
# type:(Text, Int, Int, Text) -> Int
# TODO: get confirmation working on maya 2018
anim_source_name = cmds.timeEditorClip(clip_index, q=True, animSource=True)
try:
# コールバックでよばれるUI ポップ関数を強制上書き
# なにも実行しない関数にしてしまう
overwrite_cmd = """global proc teRosterMappingWindow(string $animSource, int $clipId, string $continueCmd, string $remapNS){{}}"""
mel.eval(overwrite_cmd)
remap_cmd = """teRemapAnimSourceToNamespace {} :{};""".format(anim_source_name, remap_to)
start_time = cmds.getAttr("{}.start".format(anim_source_name))
duration = cmds.getAttr("{}.duration".format(anim_source_name))
remap_cmd = """teRemapToNamespace {} :{} {} {} {};""".format(
anim_source_name,
remap_to,
start_time,
duration,
"{}_{}".format(anim_source_name, remap_to)
)
mel.eval(remap_cmd)
cmds.timeEditorTracks(track_container_name, e=True, removeTrack=track_index)
remapped_clip_index = clip_index + 1
# 本来 UI経由で呼ばれる処理をここで行う
sources = cmds.timeEditorAnimSource(anim_source_name, q=True, targets=True)
for s in sources:
src = s
dst = "{}:{}".format(remap_to, s.split(":")[-1])
cmds.timeEditorClip(e=True, remapSource=(dst, src), clipId=remapped_clip_index)
cmds.timeEditorClip(e=True, animSource=anim_source_name, existingOnly=True, clipId=remapped_clip_index)
for s in sources:
src = s
dst = "{}:{}".format(remap_to, s.split(":")[-1])
cmds.timeEditorClip(e=True, remap=(dst, src), clipId=clip_index + 1)
except Exception:
import traceback
traceback.print_exc()
# 強制上書きした proc をもとに戻す
source_cmd = """source teRemapToNamespace.mel;"""
mel.eval(source_cmd)
raise
return remapped_clip_index
def add_fbx_clip(
fbx_path,
track_container_name=None,
track_name=None,
remap_to=None
):
# type: (Text, Text, Text, Text) -> Int, Int
"""FBX を レファレンスしTrackEditor のクリップとして読み込む。
読み込んだ FBXは remap_to で指定したネームスペースへリマップされる
"""
if not track_container_name:
track_container_name = DEFAULT_TRACK_CONTAINER_NAME
create_composition_track(track_container_name)
# fbx をネームスペース付きで読み込む。ネームスペースはファイル名からとる
fbx_path = fbx_path.replace(os.sep, "/")
clip_name = os.path.basename(fbx_path).split(".")[0]
nodes = cmds.file(fbx_path, r=True, f=True, namespace=clip_name, returnNewNodes=True)
# unsafeなファイル名な場合勝手に変更かkるので実際に読み込まれたネームスペースを取得
animation_ns = next(x for x in nodes if (":" in x and x.startswith("|")))
animation_ns = animation_ns.split(":")[0]
track_path = "{}:-1".format(track_container_name)
track_index, clip_index = create_clip(track_path, nodes, clip_name)
#
if not remap_to:
remap_to = ":(root)"
remapped_clip_index = remap_clip(track_container_name, track_index, clip_index, remap_to)
remapped_track_index = cmds.timeEditorClip(remapped_clip_index, q=True, track=True)
remapped_track_index = int(remapped_track_index.split(":")[-1])
# rename the track
if track_name is not None:
cmds.timeEditorTracks(
track_container_name,
e=True,
trackIndex=remapped_track_index,
trackName=track_name
)
return remapped_track_index, remapped_clip_index
def get_track_name(track):
return cmds.timeEditorTracks(track, q=True, trackName=True)
def get_track_name_by_index(index, track_container_name=None):
if not track_container_name:
track_container_name = DEFAULT_TRACK_CONTAINER_NAME
return cmds.timeEditorTracks(track_container_name, q=True, trackIndex=index, trackName=True)
def get_clip_offset(index, track_container_name=None):
if not track_container_name:
track_container_name = DEFAULT_TRACK_CONTAINER_NAME
original_start, _ = get_clip_original_time_range(index, track_container_name)
current_start, _, curve_start = get_clip_time_range(index, track_container_name)
return original_start - current_start - curve_start
def get_clip_time_range(index, track_container_name=None):
if not track_container_name:
track_container_name = DEFAULT_TRACK_CONTAINER_NAME
source_name = cmds.timeEditorClip(index, q=True, absolute=True, clipNode=True)
current_start = cmds.timeEditorClip(index, q=True, absolute=True, startTime=True)
current_duration = cmds.timeEditorClip(index, q=True, absolute=True, duration=True)
current_curve_start = cmds.getAttr("{}.clip[0].curveStart".format(source_name))
return current_start, current_start + current_duration, current_curve_start
def get_clip_original_time_range(index, track_container_name=None):
if not track_container_name:
track_container_name = DEFAULT_TRACK_CONTAINER_NAME
source_name = cmds.timeEditorClip(index, q=True, absolute=True, clipNode=True)
current_start = cmds.timeEditorClip(index, q=True, absolute=True, startTime=True)
current_duration = cmds.timeEditorClip(index, q=True, absolute=True, duration=True)
current_curve_start = cmds.getAttr("{}.clip[0].curveStart".format(source_name))
cmds.timeEditorClip(clipId=index, e=True, resetTiming=True)
original_start = cmds.timeEditorClip(index, q=True, absolute=True, startTime=True)
original_end = cmds.timeEditorClip(index, q=True, absolute=True, endTime=True)
cmds.setAttr("{}.clip[0].curveStart".format(source_name), current_curve_start)
cmds.setAttr("{}.clip[0].clipStart".format(source_name), current_start)
cmds.setAttr("{}.clip[0].clipDuration".format(source_name), current_duration)
return original_start, original_end
def reset_timing_all_clips(track_container_name=None):
for t in foreach_tracks(track_container_name):
clip_indice = cmds.timeEditorTracks(t, allClips=True, q=True) or []
for clip_id in clip_indice:
# print(t, cmds.timeEditorClip(clip_id, q=True, truncated=True, start=True))
data_type = cmds.timeEditorClip(clip_id, q=True, clipDataType=True)
if data_type != 0:
continue
cmds.timeEditorClip(clipId=clip_id, e=True, resetTiming=True)
def foreach_tracks(track_container_name=None):
# type: (Text) -> Generator[Text]
"""Yields immediate track in the format "tracksNode:trackIndex" (["Composition1:0", "Composition1:1", ...])."""
if not track_container_name:
track_container_name = DEFAULT_TRACK_CONTAINER_NAME
tracks = cmds.timeEditorComposition(q=True, allCompositions=True) or []
if track_container_name not in tracks:
return
all_track = cmds.timeEditorTracks(track_container_name, allTracks=True, q=True) or []
for t in all_track:
yield t
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment