maya 约束批量导入导出
?
今天整了一整天maya中的約束節(jié)點的導入和導出,因為動畫需求,需要將舊文件中的所有約束信息導出,并導入到新文件中。其實MGTools3.0中就有該功能,用過的同學應該都知道這個:
這個可以說是非常方便了,不管約束類型,偏移,還有約束關鍵幀,絕大多數情況下都能準確跨maya復制過去。然而某些情況還是會出現問題,比如:A和B同時對C進行parent約束,不過A只約束translateX,而B只約束translateY(雖然這種約束方式很傻逼),這種情況下約束并不能成功復制過去。而且這種復制只能針對性復制,而我們動畫的需求是,將場景中所有非引用類別的約束全部導出。
那我們可以列出所有非引用的約束節(jié)點,然后直接導出嗎?當然不可以。maya的節(jié)點網絡是牽一發(fā)動全身的,例如約束節(jié)點的連接關系長這樣:
如果直接導出約束,會將該節(jié)點所有有直接連接的節(jié)點都一起導出。
那可以直接記錄約束節(jié)點的連接信息(例如約束類別,偏移等),保存到文件里(例如json),然后在新的文件中按照相關信息重新做約束嗎?答案是可以,可惜我沒這么做,因為要考慮的東西太多了:被約束的屬性,偏移,約束類別,動畫曲線。。。
在排除上面兩個方法之后我做了如下嘗試:將所有非動畫曲線類別(也就是除了上圖左側藍色節(jié)點以外)的節(jié)點連接信息全部記錄到json,然后斷開,unparent,導出,再在新的場景中導入約束節(jié)點,讀入json重新做連接。這樣既不會導出多余的東西,節(jié)點連接信息又能全部保留。思路正確,然而一番嘗試之后我又發(fā)現了這個不幸的事實:在打斷連接之后,約束節(jié)點的1W0屬性消失了。。。泥煤,導致導入節(jié)點之后連不上該屬性,百般嘗試未果只好作罷。
?
斷開連接之后的1W0屬性消失了
只能祭出終極絕招:移花接木。
也就是將上面的思路中的斷開操作,變成:新建兩個節(jié)點replaceA,replaceB,代替之前的約束節(jié)點的上游和下游節(jié)點,將之前的操作cmds.disconnectAttr(A.a, B.b)變成cmds.connectAttr(replaceA.a, B.b,? force=True),一定要加上force=True,這樣才可以把連接強行接過去。于是乎,就相當于新建的節(jié)點代替原來的約束源被導出。但是沒關系,在新場景中恢復連接之后把自建的節(jié)點刪除就好。下面是思路圖:
變?yōu)?#xff1a;(假設新建的節(jié)點就是locator)
?
問題成功解決。
附上代碼:
#!/usr/bin/env python# -*- coding: utf-8 -*-# author: Dango Wang# time : 2019/1/29__author__ = "dango wang"import pymel.core as pmimport maya.cmds as mcimport jsonimport codecsimport osimport shutilimport loggingdef get_all_constr():"""返回場景中所有的非引用類別的約束節(jié)點,返回list"""all_constraints = [constraint for constraint in pm.ls(type="constraint") if not pm.referenceQuery(constraint, inr=1)]return all_constraintsdef get_parent_transform(node):"""返回給定的節(jié)點的父節(jié)點,返回tuple"""return node.name(), pm.listRelatives(node, p=True)[0].name()def get_source_constr(constraint_node):"""返回約束節(jié)點的約束源:類型為list"""return pm.PyNode(constraint_node).getTargetList()def get_s_connections(constraint_node):"""獲取所有上游連接,要排出1W0這個節(jié)點,因為它是自個兒連自個兒"""all_s = pm.listConnections(constraint_node,d=0,s=1,c=1,p=1)connections_dict = dict()for each_connection in all_s:if 'W0' not in each_connection[1].name() and 'W0' not in each_connection[0].name():connections_dict[each_connection[1].name()] = each_connection[0].name()return connections_dictdef get_d_connections(constraint_node):all_d = pm.listConnections(constraint_node,d=1,s=0,c=1,p=1)connections_dict = dict()for each_connection in all_d:if 'W0' not in each_connection[1].name() and 'W0' not in each_connection[0].name():connections_dict[each_connection[0].name()] = each_connection[1].name()return connections_dictdef get_all_connections(constraint_node):"""返回與該約束節(jié)點所有有關的連接,返回類型[‘上游連接’:‘被連接體’,...]"""connections_dict = dict()all_s = get_s_connections(constraint_node)all_d = get_d_connections(constraint_node)connections_dict.update(all_s)connections_dict.update(all_d)return connections_dictdef remove_anicrv_nodes(connections_dict):"""將所有的連接信息中的動畫曲線節(jié)點排除,因為動畫曲線可以跟隨約束導出。返回類型dict"""if not connections_dict:return Falsenew_connections_dict = dict()anicrv_list = ['animCurveTL', 'animCurveTA', 'animCurveTU']# print connections_dictfor k, v in connections_dict.items():# print k, vif (pm.nodeType(k) in anicrv_list) or (pm.nodeType(v) in anicrv_list):continueelse:new_connections_dict[k] = vif new_connections_dict:return new_connections_dictelse:return Falsedef break_s_connections(connections_dict):"""打斷跟約束節(jié)點有關的非動畫曲線的連接.這里踩了很多坑,直接打斷的話約束節(jié)點有些屬性會丟失,只能再創(chuàng)建個節(jié)點代替之前的連接跟隨約束節(jié)點一起導出"""if not connections_dict:return Falsenew_node = mc.createNode("transform", n="dango_constraints_record_temp_node")for k, v in connections_dict.items():try:# print k,vs_node_name = k.split(".")[0]attr = k.split(".")[1]if not mc.objExists(new_node+'.'+attr):mc.addAttr(new_node, shortName=attr, longName=attr)new_k = k.replace(s_node_name, new_node)print mc.connectAttr(new_k, v, f=1)?? ?try:?? ??? ?mc.disconnectAttr(k, v)except:passexcept RuntimeError:continuereturn Truedef break_d_connections(connections_dict):"""同上"""if not connections_dict:return Falsenew_node = mc.createNode("transform", n="dango_constraints_record_temp_node")for k, v in connections_dict.items():try:# print k,vs_node_name = v.split(".")[0]attr = v.split(".")[1]if not mc.objExists(new_node+'.'+attr):mc.addAttr(new_node, shortName=attr, longName=attr)new_v = v.replace(s_node_name, new_node)print mc.connectAttr(k, new_v, f=1)?? ??? ??? ?try:?? ??? ?mc.disconnectAttr(k, v)except:passexcept RuntimeError:continuereturn Truedef add_connections(connections_dict):"""重新建立字典中兩個節(jié)點之間的連接"""if not connections_dict:return Falsenot_found_connections = list()for k, v in connections_dict.items():if not pm.objExists(k):not_found_connections.append(k)continueif not pm.objExists(v):not_found_connections.append(v)continuetry:# print k,vmc.connectAttr(k, v, f=1)except RuntimeError:try:mc.connectAttr(v, k, f=1)except RuntimeError:?? ??? ??? ??? ?# print k, vcontinueif not_found_connections:return not_found_connectionselse:return Falsedef get_pos(transform):translate = mc.getAttr(transform+".t")rotate = mc.getAttr(transform+".r")translate.extend(rotate)return translatedef get_all_positions(constraint_nodes):# 獲取所有約束相關的物體的位置,在重建約束之前要先把位置打回去mc.currentTime(101)all_transforms = list()all_positions = dict()all_sources = map(get_source_constr, constraint_nodes)all_destis = [get_parent_transform(each_info)[1] for each_info in constraint_nodes]for i in all_sources:all_transforms.extend(i)all_transforms = [k.name() for k in all_transforms]all_transforms.extend(all_destis)for each_transform in all_transforms:all_positions[each_transform] = get_pos(each_transform)return all_positionsdef export_constraints(path):"""將場景中所有處理完之后的約束節(jié)點導出,同時輸出連接及父子信息"""# 創(chuàng)建輸出文件parent_info_path = path + '/parent_info.json'connections_dict_path = path + '/connections_dict.json'offset_info_path = path + '/offset.json'constraints_ma = path + '/constraints.ma'# 獲取相關信息all_constraints = get_all_constr()if not all_constraints:return Falseparent_info = map(get_parent_transform, all_constraints)all_connections_info_temp = map(get_all_connections, all_constraints)all_connections_info = map(remove_anicrv_nodes, all_connections_info_temp)all_positions_info = get_all_positions(all_constraints)print all_connections_info# 打斷連接及unparent# map(break_connections, all_connections_info)all_s = map(get_s_connections, all_constraints)all_d = map(get_d_connections, all_constraints)map(break_s_connections, all_s)map(break_d_connections, all_d)# print all_constraints# 這里有一個坑,如果直接pm.Unparent(all_constraints)的話會提示找不到目標mc.select(all_constraints, r=True)if mc.ls(sl=True):mc.Unparent()# 寫入文件with codecs.open(parent_info_path, 'w', 'utf-8') as p:json.dump(parent_info, p)with codecs.open(connections_dict_path, 'w', 'utf-8') as c:json.dump(all_connections_info, c)with codecs.open(offset_info_path, 'w', 'utf-8') as t:json.dump(all_positions_info, t)# 導出約束節(jié)點mc.file(constraints_ma, es=1, force=1, typ="mayaAscii", options="v=0;p=17;f=0")pm.delete(all_constraints)pm.delete("dango_constraints_record_temp_node*")logging.info(u'成功導出文件:{}{}{}{}'.format(parent_info_path, connections_dict_path, constraints_ma,offset_info_path))def import_constraints(path):"""導入所有的約束節(jié)點以及連接信息并重新進行連接parent_info長這樣:[(node,parent),(node,parent),(node,parent),...]all_connections_info長這樣:[{k:v,k:v,...},{k:v,k:v,...},{k:v,...},...]"""# 讀入信息mc.currentTime(101)parent_info_path = path + '/parent_info.json'connections_dict_path = path + '/connections_dict.json'constraints_ma = path + '/constraints.ma'offset_info_path = path + '/offset.json'with codecs.open(parent_info_path, 'r', 'utf-8') as p:parent_info = json.load(p)# print parent_infowith codecs.open(connections_dict_path, 'r', 'utf-8') as c:# print type(c)all_connections_info = json.load(c)with codecs.open(offset_info_path, 'r', 'utf-8') as t:all_positions_info = json.load(t)# 判斷一下約束節(jié)點的父節(jié)點在不在,重新parentall_parents = [each[1] for each in parent_info]not_found_parents = [notfound for notfound in all_parents if not pm.objExists(notfound)]if not_found_parents:error_info = [error for error in not_found_parents]logging.error(u'以下節(jié)點未找到!請確保文件整理前后命名一致:%s' % error_info)return?mc.file(constraints_ma,i=True)for each_tuple in parent_info:mc.parent(each_tuple[0], each_tuple[1])# ?將所有約束相關的物體放到原位for each_obj, each_pos in all_positions_info.items():# print ?each_posmc.setAttr(each_obj+".t", each_pos[0][0], each_pos[0][1], each_pos[0][2])mc.setAttr(each_obj+".r", each_pos[1][0], each_pos[1][1], each_pos[1][2])# 增加連接,找不到的連接節(jié)點將被返回connections = map(add_connections, all_connections_info)# print connectionserror_connections = list()for each_connect in connections:if each_connect:error_connections.extend(each_connect)if error_connections:error_info2 = [error2 for error2 in error_connections]logging.error(u'以下節(jié)點未找到!請確保文件整理前后命名一致:%s' % error_info2)return False# if mc.objExists("dango_constraints_record_temp_node*"):# ?? ?mc.delete("dango_constraints_record_temp_node*")return Trueif __name__ == '__main__':# 導出場景中所有的約束節(jié)點和相關信息export_constraints("D:/dango_repo/constraint_exp_imp/test")# 導入所有的約束節(jié)點和重建相關連接import_constraints("D:/dango_repo/constraint_exp_imp/test")# 刪除臨時節(jié)點(直接寫在import_constraints中的話不成功,可能因為map執(zhí)行效率低導致):if mc.objExists("dango_constraints_record_temp_node*"):mc.delete("dango_constraints_record_temp_node*")# 刪除導出的文件(將整個文件夾刪除)shutil.rmtree("D:/dango_repo/constraint_exp_imp/test")?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的maya 约束批量导入导出的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 卷积神经网络学习心得
- 下一篇: 使用自定义数据绘制脑地形矩阵图