maya python插件_Maya Python - ALembic导入导出助手

前言Abc助手其实最开始是师兄跟我提的。

我记得刚到公司的第一天,师兄就让我去研究如何实现批量将材质信息上到每个面上。

这就为这个插件的诞生慢下了伏笔。

其实就我自己而言,我当时并不太理解师兄想要实现的效果。

不过就是这么凑巧的,我们的三维动画实训也遇到了Alembic导出到Houdini的材质问题。

当时咨询了师兄之后才知道,原来问题都是一样的。

而这些信息从模型信息写为模型的面信息就是出于这个考虑。

师兄提供了一篇很好的推文,上面详细地说明了这个导出的解决方案。

我就是基于这篇推文的思路进行开发的。

界面开发这次是首次尝试使用 Qt Designer 来开完整的插件,不过之前也用PyQt开发软件也积累了不少的经验。

下面就是首次使用 Qt Designer 开发的插件界面

在Maya打开的界面是这个样子的

其实 Qt Designer 大大减低了界面开发的难度

UI功能开发这次我不仅开发出了可折叠式的UI按钮界面,还开发了可以存储最后一次设置的json功能。

折叠式UI开发其实原理很简单,我当时在制作PLB下载器的时候就有做过类似的效果。

原理就是点击按钮的时候将QWidget的setvisibled函数设置为False

如此一来按钮折叠式的内容就会消失。

然而因为消失的区域,会导致内容空缺,会导致按钮变得非常松散。

因此我想到的解决方案就是,每一次点击按钮都重置界面大小,如此一来就可以解决这个问题。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20class Interface(base_class,form_class):

def __init__(self):

"""

初始化相关的代码

"""

self.NameSapce_Toggle_Check = True

self.NameSpace_Toggle.clicked.connect(self.NameSapce_Toggle_Fun)

def NameSapce_Toggle_Fun(self):

if self.NameSapce_Toggle_Check:

self.NameSpace_Btn.setVisible(False)

self.NameSapce_Toggle_Check = False

self.NameSpace_Toggle.setText(u"■命名空间 - 修正")

else:

self.NameSpace_Btn.setVisible(True)

self.NameSapce_Toggle_Check = True

self.NameSpace_Toggle.setText(u"▼命名空间 - 修正")

self.adjustSize()

json存储记录功能我的出发点就是能够让插件记录上一次设置的选项。

于是我参考了 Python For Maya Artist Friendly Programming 教程的写法

每一次关闭窗口时候都会触发json数据的存储记录

而每一次打开窗口的时候都会读取相关的json,如果没有json数据就跳过。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51class Interface(base_class,form_class):

def __init__(self):

"""

初始化相关的代码

"""

# 判断当前路径是否存在json数据可以读取

if os.path.exists(GUI_state_PATH):

GUI_state = {}

with open(GUI_state_PATH,'r') as f:

GUI_state = json.load(f)

# 按照存储的json数据设置插件

self.Verbose_CheckBox.setChecked(GUI_state['Verbose_CheckBox'])

self.NM__CheckBox.setChecked(GUI_state['NM__CheckBox'])

self.Render_CheckBox.setChecked(GUI_state['Render_CheckBox'])

self.Namespace_CheckBox.setChecked(GUI_state['Namespace_CheckBox'])

self.UV_CheckBox.setChecked(GUI_state['UV_CheckBox'])

self.Color_CheckBox.setChecked(GUI_state['Color_CheckBox'])

self.Render_CheckBox.setChecked(GUI_state['Render_CheckBox'])

self.WS__CheckBox.setChecked(GUI_state['WS__CheckBox'])

self.Vis__CheckBox.setChecked(GUI_state['Vis__CheckBox'])

self.NameSapce_Toggle_Check = GUI_state['NameSapce_Toggle_Check']

self.Export_Toggle_Check = GUI_state['Export_Toggle_Check']

self.Convert_Toggle_Check = GUI_state['Convert_Toggle_Check']

# 切换到正确的按钮方式

self.NameSapce_Toggle_Fun()

self.Export_Toggle_Fun()

self.Convert_Toggle_Fun()

self.NameSapce_Toggle_Fun()

self.Export_Toggle_Fun()

self.Convert_Toggle_Fun()

# 关闭窗口时保存当前视窗选择

def closeEvent(self, event):

# 存储当前界面选择

GUI_state = {}

GUI_state['Verbose_CheckBox'] = self.Verbose_CheckBox.isChecked()

GUI_state['NM__CheckBox'] = self.NM__CheckBox.isChecked()

GUI_state['Render_CheckBox'] = self.Render_CheckBox.isChecked()

GUI_state['Namespace_CheckBox'] = self.Namespace_CheckBox.isChecked()

GUI_state['UV_CheckBox'] = self.UV_CheckBox.isChecked()

GUI_state['Color_CheckBox'] = self.Color_CheckBox.isChecked()

GUI_state['WS__CheckBox'] = self.WS__CheckBox.isChecked()

GUI_state['Vis__CheckBox'] = self.Vis__CheckBox.isChecked()

GUI_state['NameSapce_Toggle_Check'] = self.NameSapce_Toggle_Check

GUI_state['Export_Toggle_Check'] = self.Export_Toggle_Check

GUI_state['Convert_Toggle_Check'] = self.Convert_Toggle_Check

with open(GUI_state_PATH,'w') as f:

json.dump(GUI_state,f,indent=4)

主功能开发

按面给材质正如推文所说的,Houdini的Alembic需要保留Maya的面组信息来记录材质。

只要面组信息是对的,场景中有相关的材质,导入Alembic的时候,Maya会自动连接起来。

问题在于,如果某些材质是对物体进行着色的话,那么导入Maya是没有面组信息的。

因此解决方案就是将物体着色转为对物体的每个面进行着色。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40def Convert_Fun(self):

# 获取当前选择

sel = cmds.ls(sl=True,fl=True)

# 循环当前选择

for obj in sel:

# 获取SG节点

shapeNode = cmds.listRelatives(obj,children=True,shapes=True)

SGNodeList = cmds.listConnections(shapeNode[0],type="shadingEngine")

SGNodeList = list(set(SGNodeList))

# 循环SG节点

for SGNode in SGNodeList:

# 通过SG节点获取材质球

shader = cmds.listConnections(SGNode + ".surfaceShader")

cmds.select(cl=True)

# 选择材质球赋予的物体

cmds.hyperShade( objects=shader[0] )

# 将当前选择转换为面

cmds.ConvertSelectionToFaces()

faceList = cmds.ls(sl=True,fl=True)

cmds.sets(cl=(shader[0]+"SG"))

# 循环材质上的面重新赋予材质

for face in faceList :

if obj == face.split('.')[0]:

cmds.select(face)

cmds.sets(add=(shader[0]+"SG"))

mel.eval("maintainActiveChangeSelectMode " + sel[-1] + ";")

cmds.select(cl=True)

cmds.headsUpMessage( u'转换成功' )

模型材质传递这个功能是给现有的完全相同的来个物体传递两者的材质

这个写法其实参照公司之前写好的脚本修改的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30def Transfer_Fun(self):

sel = cmds.ls(sl=True,fl=True)

shapeNode = cmds.listRelatives(sel[0],children=True,shapes=True)

SGNodeList = cmds.listConnections(shapeNode[0],type="shadingEngine")

SGNodeList = list(set(SGNodeList))

for each in SGNodeList:

cmds.hyperShade(objects=each)

sel_mat_face=cmds.ls(sl=True)

##剔除不是本物体的面 (按材质组选的面,有可能选择其他物体)

mat_face_use=[]

for each_face in sel_mat_face:

if each_face.find(sel[0])!=-1: ##没有找到就返回-1

print each_face

mat_face_use.append(each_face)

print mat_face_use

##改为目标物体的面

mat_face_obj=[]

for each_new in mat_face_use:

mat_face_obj.append( each_new.replace(sel[0],sel[1]) )

cmds.select( mat_face_obj , r=True )

cmds.hyperShade( assign = each )

cmds.select(cl=True)

cmds.headsUpMessage( u'传递成功' )

Alembic 勾选相关参数导出要设置选项中的参数其实我是完全没有头绪的。

毕竟这个东西的勾选是没有任何命令回显的,所以最初完全没有头绪。

但是想起ADV5绑定的时候确实是可以设置相关的数据的。

于是我就参考了ADV5绑定设置中的源码,找到了蛛丝马迹。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31def Alembic_Export_Fun(self):

check = self.Check_CheckBox(self.UV_CheckBox.isChecked())

cmds.optionVar(iv=('Alembic_exportUVWrite',check))

check = self.Check_CheckBox(self.Face_CheckBox.isChecked())

cmds.optionVar(iv=('Alembic_exportWriteFaceSets',check))

check = self.Check_CheckBox(self.Verbose_CheckBox.isChecked())

cmds.optionVar(iv=('Alembic_exportVerbose',check))

check = self.Check_CheckBox(self.Color_CheckBox.isChecked())

cmds.optionVar(iv=('Alembic_exportWriteColorSets',check))

check = self.Check_CheckBox(self.Render_CheckBox.isChecked())

cmds.optionVar(iv=('Alembic_exportRenderableOnly',check))

check = self.Check_CheckBox(self.WS__CheckBox.isChecked())

cmds.optionVar(iv=('Alembic_exportWorldSpace',check))

check = self.Check_CheckBox(self.NM__CheckBox.isChecked())

cmds.optionVar(iv=('Alembic_exportNoNormals',check))

check = self.Check_CheckBox(self.Vis__CheckBox.isChecked())

cmds.optionVar(iv=('Alembic_exportWriteVisibility',check))

check = self.Check_CheckBox(self.Namespace_CheckBox.isChecked())

cmds.optionVar(iv=('Alembic_exportStripNamespaces',check))

# 触发 Alembic 导出

cmds.AlembicExportSelection()

使用 cmds.optionVar(l=True) 可以查看optionVar相关的属性变量

install.mel 的搭建鉴于对ADV5的安装方法的好奇,我也想开发出这种方便的安装方式。

本来我是想尝试直接将开发好的py文件扔进Maya,看看能不能如我所愿地执行插件。

经过测试,Maya2017是完全没问题的,但是Maya2015就无法这样直接执行了。

所以参考了ADV5的方法,我发现它是通过Mel来实现的

于是,对ADV5的install.mel进行魔改,成功实现了好ADV5差不多的效果。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64string $subDir = "abc_helper/";

string $scriptName="main";

string $scirptExt = "py";

string $ImagePath = "/icon/alembic_logo" ;

global string $gShelfTopLevel;

string $currentShelf = `tabLayout -query -selectTab $gShelfTopLevel`;

setParent $currentShelf;

string $asInstallScriptLocation=`asInstallScriptLocation`;

string $command="import sys\nsys.path.append(\"" + $asInstallScriptLocation + $subDir + "\")\nimport " + $scriptName + "\nreload(" + $scriptName + ") ";

string $sourceFile=$asInstallScriptLocation+ $subDir + $scriptName+"."+$scirptExt;

string $iconExt="jpg";

if (`asMayaVersionAsFloat`<2012)

$iconExt="xpm";

string $icon=$asInstallScriptLocation + $subDir + $ImagePath + "."+$iconExt;

if (!`file -q -ex $sourceFile`)

error ("Something went wrong, can not find: \""+$sourceFile+"\"");

shelfButton

-command $command

-annotation $scriptName

-label $scriptName

-image $icon

-image1 $icon

-sourceType "python"

;

global proc asInstallScriptLocator (){}

global proc string asInstallScriptLocation ()

{

string $whatIs=`whatIs asInstallScriptLocator`;

string $fullPath=`substring $whatIs 25 999`;

string $buffer[];

string $slash="/";

if (`gmatch $whatIs "*\\\\*"`)//sourced from ScriptEditor

$slash="\\";

int $numTok=`tokenize $fullPath $slash $buffer`;

int $numLetters=size($fullPath);

int $numLettersLastFolder=size($buffer[$numTok-1]);

string $scriptLocation=`substring $fullPath 1 ($numLetters-$numLettersLastFolder)`;

return $scriptLocation;

}

global proc float asMayaVersionAsFloat ()

{

float $version=2012;

if (`exists getApplicationVersionAsFloat`)

return `getApplicationVersionAsFloat`;

string $versionString=`about -v`;

string $tempString[];

string $char;

tokenize $versionString $tempString;

//default to 2012, if versionString is not all numbers

for ($i=0;$i

{

$char=`substring $tempString[0] ($i+1) ($i+1)`;

if (!`gmatch $char "[0-9]"`)

return 2012;

}

$version=$tempString[0];

return $version;

}

总结以上就是 Alembic导入导出助手 的核心源码了。

原理其实一点都不复杂,整个制作过程中最困难的是想出按面给材质的方法以及最后怎么实现设置导出选项。

这个插件开发奠定自己的插件开发风格,为后面的藤蔓生长工具的开发奠定了坚实的基础。

2018-11-30 更新借助UI2CG工具更新了基础界面和功能

这次删除了命名空间修改的功能,因为我发现Maya自带命名空间编辑器,比我自己的开发的小功能强多了。

这次当然也加入了UI2CG所带来的所有特性。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的Maya导出ABC文件的Python插件: ```python import maya.cmds as cmds import maya.api.OpenMaya as OpenMaya def export_abc(*args): # 获取当前选中的物体 selection = cmds.ls(selection=True) if not selection: cmds.warning("请选择要导出的物体") return # 获取导出路径 file_path = cmds.fileDialog2(fileFilter='Alembic Files (*.abc)', dialogStyle=2, fileMode=0) if not file_path: return # 获取导出开始和结束帧数 start_frame = cmds.playbackOptions(query=True, minTime=True) end_frame = cmds.playbackOptions(query=True, maxTime=True) # 创建导出器 exporter = OpenMaya.MFnPluginData().create('mayaUsdExport', 'mayaUsdExport') exporter_node = OpenMaya.MFnDependencyNode().create('mayaUsdExport') exporter.setObject(exporter_node) # 设置导出参数 cmds.setAttr(exporter_node + '.filePath', file_path[0], type='string') cmds.setAttr(exporter_node + '.startFrame', start_frame) cmds.setAttr(exporter_node + '.endFrame', end_frame) cmds.setAttr(exporter_node + '.selection', True) # 执行导出 cmds.AbcExport(j=exporter.name(), selection=True) cmds.confirmDialog(title='导出ABC文件', message='导出成功', button=['确定']) # 注册命令 cmds.commandPort(name=':12345', sourceType='python') cmds.commandPort(name=':54321', sourceType='python') cmds.commandPort(name=':87654', sourceType='python') cmds.commandPort('12345', echoOutput=True, noreturn=False) cmds.commandPort('54321', echoOutput=True, noreturn=False) cmds.commandPort('87654', echoOutput=True, noreturn=False) cmds.commandProc('exportAbc', 'python', 'export_abc') ``` 这个插件会弹出一个文件对话框,让用户选择导出路径,然后将当前选中的物体导出ABC文件。导出器使用的是MayaAlembic导出器,导出的范围是选中的物体,并设置了导出的开始和结束帧数。如果导出成功,会弹出一个确认对话框。 注意,这个插件需要在Maya中运行,可以通过将代码保存为.py文件,然后在Maya的Script Editor中运行来使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值