maya 约束批量导入导出
?
今天整了一整天maya中的約束節(jié)點(diǎn)的導(dǎo)入和導(dǎo)出,因?yàn)閯?dòng)畫(huà)需求,需要將舊文件中的所有約束信息導(dǎo)出,并導(dǎo)入到新文件中。其實(shí)MGTools3.0中就有該功能,用過(guò)的同學(xué)應(yīng)該都知道這個(gè):
這個(gè)可以說(shuō)是非常方便了,不管約束類型,偏移,還有約束關(guān)鍵幀,絕大多數(shù)情況下都能準(zhǔn)確跨maya復(fù)制過(guò)去。然而某些情況還是會(huì)出現(xiàn)問(wèn)題,比如:A和B同時(shí)對(duì)C進(jìn)行parent約束,不過(guò)A只約束translateX,而B(niǎo)只約束translateY(雖然這種約束方式很傻逼),這種情況下約束并不能成功復(fù)制過(guò)去。而且這種復(fù)制只能針對(duì)性復(fù)制,而我們動(dòng)畫(huà)的需求是,將場(chǎng)景中所有非引用類別的約束全部導(dǎo)出。
那我們可以列出所有非引用的約束節(jié)點(diǎn),然后直接導(dǎo)出嗎?當(dāng)然不可以。maya的節(jié)點(diǎn)網(wǎng)絡(luò)是牽一發(fā)動(dòng)全身的,例如約束節(jié)點(diǎn)的連接關(guān)系長(zhǎng)這樣:
如果直接導(dǎo)出約束,會(huì)將該節(jié)點(diǎn)所有有直接連接的節(jié)點(diǎn)都一起導(dǎo)出。
那可以直接記錄約束節(jié)點(diǎn)的連接信息(例如約束類別,偏移等),保存到文件里(例如json),然后在新的文件中按照相關(guān)信息重新做約束嗎?答案是可以,可惜我沒(méi)這么做,因?yàn)橐紤]的東西太多了:被約束的屬性,偏移,約束類別,動(dòng)畫(huà)曲線。。。
在排除上面兩個(gè)方法之后我做了如下嘗試:將所有非動(dòng)畫(huà)曲線類別(也就是除了上圖左側(cè)藍(lán)色節(jié)點(diǎn)以外)的節(jié)點(diǎn)連接信息全部記錄到j(luò)son,然后斷開(kāi),unparent,導(dǎo)出,再在新的場(chǎng)景中導(dǎo)入約束節(jié)點(diǎn),讀入json重新做連接。這樣既不會(huì)導(dǎo)出多余的東西,節(jié)點(diǎn)連接信息又能全部保留。思路正確,然而一番嘗試之后我又發(fā)現(xiàn)了這個(gè)不幸的事實(shí):在打斷連接之后,約束節(jié)點(diǎn)的1W0屬性消失了。。。泥煤,導(dǎo)致導(dǎo)入節(jié)點(diǎn)之后連不上該屬性,百般嘗試未果只好作罷。
?
斷開(kāi)連接之后的1W0屬性消失了
只能祭出終極絕招:移花接木。
也就是將上面的思路中的斷開(kāi)操作,變成:新建兩個(gè)節(jié)點(diǎn)replaceA,replaceB,代替之前的約束節(jié)點(diǎn)的上游和下游節(jié)點(diǎn),將之前的操作cmds.disconnectAttr(A.a, B.b)變成cmds.connectAttr(replaceA.a, B.b,? force=True),一定要加上force=True,這樣才可以把連接強(qiáng)行接過(guò)去。于是乎,就相當(dāng)于新建的節(jié)點(diǎn)代替原來(lái)的約束源被導(dǎo)出。但是沒(méi)關(guān)系,在新場(chǎng)景中恢復(fù)連接之后把自建的節(jié)點(diǎn)刪除就好。下面是思路圖:
變?yōu)?#xff1a;(假設(shè)新建的節(jié)點(diǎn)就是locator)
?
問(wèn)題成功解決。
附上代碼:
#!/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():"""返回場(chǎng)景中所有的非引用類別的約束節(jié)點(diǎn),返回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é)點(diǎn)的父節(jié)點(diǎn),返回tuple"""return node.name(), pm.listRelatives(node, p=True)[0].name()def get_source_constr(constraint_node):"""返回約束節(jié)點(diǎn)的約束源:類型為list"""return pm.PyNode(constraint_node).getTargetList()def get_s_connections(constraint_node):"""獲取所有上游連接,要排出1W0這個(gè)節(jié)點(diǎn),因?yàn)樗亲詡€(gè)兒連自個(gè)兒"""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é)點(diǎn)所有有關(guān)的連接,返回類型[‘上游連接’:‘被連接體’,...]"""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):"""將所有的連接信息中的動(dòng)畫(huà)曲線節(jié)點(diǎn)排除,因?yàn)閯?dòng)畫(huà)曲線可以跟隨約束導(dǎo)出。返回類型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é)點(diǎn)有關(guān)的非動(dòng)畫(huà)曲線的連接.這里踩了很多坑,直接打斷的話約束節(jié)點(diǎn)有些屬性會(huì)丟失,只能再創(chuàng)建個(gè)節(jié)點(diǎn)代替之前的連接跟隨約束節(jié)點(diǎn)一起導(dǎo)出"""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):"""重新建立字典中兩個(gè)節(jié)點(diǎn)之間的連接"""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):# 獲取所有約束相關(guān)的物體的位置,在重建約束之前要先把位置打回去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):"""將場(chǎng)景中所有處理完之后的約束節(jié)點(diǎn)導(dǎo)出,同時(shí)輸出連接及父子信息"""# 創(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'# 獲取相關(guān)信息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# 這里有一個(gè)坑,如果直接pm.Unparent(all_constraints)的話會(huì)提示找不到目標(biāo)mc.select(all_constraints, r=True)if mc.ls(sl=True):mc.Unparent()# 寫(xiě)入文件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)# 導(dǎo)出約束節(jié)點(diǎn)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'成功導(dǎo)出文件:{}{}{}{}'.format(parent_info_path, connections_dict_path, constraints_ma,offset_info_path))def import_constraints(path):"""導(dǎo)入所有的約束節(jié)點(diǎn)以及連接信息并重新進(jìn)行連接parent_info長(zhǎng)這樣:[(node,parent),(node,parent),(node,parent),...]all_connections_info長(zhǎng)這樣:[{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é)點(diǎn)的父節(jié)點(diǎn)在不在,重新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é)點(diǎn)未找到!請(qǐng)確保文件整理前后命名一致:%s' % error_info)return?mc.file(constraints_ma,i=True)for each_tuple in parent_info:mc.parent(each_tuple[0], each_tuple[1])# ?將所有約束相關(guān)的物體放到原位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é)點(diǎn)將被返回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é)點(diǎn)未找到!請(qǐng)確保文件整理前后命名一致:%s' % error_info2)return False# if mc.objExists("dango_constraints_record_temp_node*"):# ?? ?mc.delete("dango_constraints_record_temp_node*")return Trueif __name__ == '__main__':# 導(dǎo)出場(chǎng)景中所有的約束節(jié)點(diǎn)和相關(guān)信息export_constraints("D:/dango_repo/constraint_exp_imp/test")# 導(dǎo)入所有的約束節(jié)點(diǎn)和重建相關(guān)連接import_constraints("D:/dango_repo/constraint_exp_imp/test")# 刪除臨時(shí)節(jié)點(diǎn)(直接寫(xiě)在import_constraints中的話不成功,可能因?yàn)閙ap執(zhí)行效率低導(dǎo)致):if mc.objExists("dango_constraints_record_temp_node*"):mc.delete("dango_constraints_record_temp_node*")# 刪除導(dǎo)出的文件(將整個(gè)文件夾刪除)shutil.rmtree("D:/dango_repo/constraint_exp_imp/test")?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的maya 约束批量导入导出的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 卷积神经网络学习心得
- 下一篇: 使用自定义数据绘制脑地形矩阵图