Created
December 30, 2018 14:48
-
-
Save yamahigashi/3eba581df545d861088796c32e4960c4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- 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