用maya的api创建自定义的节点Creating Custom Locator

原文地址 http://www.fevrierdorian.com/blog/post/2010/02/12/Creating-custom-locator-with-Maya-s-Python-API

这个帖子是这个帖子的中文翻译。 希望这个教程对你有帮助。

正如 我之前提到的,我开始使用Maya API Python绑定。所以我看了一下Rob Bateman的的源码(一年前我觉得很难理解),然后把它们 "翻译 "成了Python…所以我创建了一个带有自定义定位器的小脚本。这显然不是世界上最简单的东西,多亏了其他教程,已经集成到Maya中的Python "插件 "和我做的OpenGL教程,我确实有了一些基础),但一旦代码运行起来,修改它来制作自己的定位器是相当有趣的。

所以,在菜单上我们有:

  • OpenGL (那么,你还怎么画你的定位器呢? )
  • 颜色的变化作为定位器选择状态的函数。

虽然不多,但你会发现这已经够用了!

脚本是没有意义的,是给新手看的!

不!如果你这么想,那你就什么都没看懂。这两者是密切相关的。如果我选择了定位器这个例子,不仅仅是因为它的 “酷”,还因为它是一个只有用API才能做的事情的例子…

但API并不是万能的!

毫无疑问,你已经知道,Maya界面完全是用MEL编写的,它允许脚本作者为自己的脚本快速创建一个GUI。不是所有的软件包都提供这种功能(有人说过XSI吗),而Maya是为数不多的几个拥有这种功能的软件包之一。

大多数的Maya函数调用也是脚本化的。这意味着你可以 "批处理 "你的操作(当你有50个场景要打开来改变一个属性的值时,你就不会笑得那么厉害了!

我只能请大家阅读我的导师的帖子关于这个问题的帖子(显示C++相当于MEL的代码)。

最后说一句话。Python是一门脚本语言! 即使它可以被解释器 “预编译”,但它仍然是脚本。

话虽如此,但我认为这个论点有点无济于事,因为即使你用Maya API Python绑定的脚本,一旦了解了基础知识,你就会意识到,一段C++代码和Python中的等效代码在语法上的差异是十分小的。

下面是一个变量声明的小例子。

C++

MFnPlugin fnPlugin(obj)

Python

fnPlugin = OpenMayaMPx.MFnPlugin(obj)

我认为C++仍然是最容易使用的,我认为(我不是因为这个例子而这么说的,而是从更广泛的角度来说),这似乎是符合逻辑的,因为它是历史上使用的实现,再加上它仍然是一种相当接近机器的语言。

那么,这一切都取决于你是否想要快速发展。编译后的代码(C++)显然会更快(约10次),但我猜想,如果你想快的话,要么你不擅长,你写的代码很烂,这让你除了求助于低级语言来提速之外,没有其他的解决办法,要么你是一个 "真正的 "开发者…如果是这样的话,我不明白你在周日的博客上做什么,你是在做什么?我!!)。

管他呢,总有一天我要在MEL、pythonForMel、C++和pythonForMayaApi之间做一个桥梁!

代码基础知识

在我们开始写定位器之前,我们需要基础代码。我们开始吧!

import sys
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaRender as OpenMayaRender
 
nodeTypeName = "myCustomLocator"
nodeTypeId = OpenMaya.MTypeId(0x87079)
 
glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
glFT = glRenderer.glFunctionTable()
 
class myNode(OpenMayaMPx.MPxLocatorNode):
	def __init__(self):
		OpenMayaMPx.MPxLocatorNode.__init__(self)
 
 
def nodeCreator():
	return OpenMayaMPx.asMPxPtr(myNode())
 
def nodeInitializer():
	return OpenMaya.MStatus.kSuccess
 
def initializePlugin(obj):
	plugin = OpenMayaMPx.MFnPlugin(obj)
	try:
		plugin.registerNode(nodeTypeName, nodeTypeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kLocatorNode)
	except:
		sys.stderr.write( "Failed to register node: %s" % nodeTypeName)
 
def uninitializePlugin(obj):
	plugin = OpenMayaMPx.MFnPlugin(obj)
	try:
		plugin.deregisterNode(nodeTypeId)
	except:
		sys.stderr.write( "Failed to deregister node: %s" % nodeTypeName)

砰!我们现在不是装得很聪明了吧?(我第一次也没装聪明!)

对了,其实也不是很复杂。解释一下:

import sys
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaRender as OpenMayaRender

这里我们要导入主要的模块。第一个(“sys”)是系统模块,我们将用它来编写插件的初始化错误信息(仅此而已)。

接下来的三个是Maya API Python模块。为了明确它们的用途,我们把它们分成了几个模块:

  • OpenMaya是与节点和命令定义相关的类以及将它们 "组装 "成插件的模块。
  • OpenMayaMPx是一个Python特有的模块。它包含Maya的代理(或MPx类)绑定。
  • OpenMayaRender是与渲染(硬件或软件)相关的类的模块。
nodeTypeName = "myCustomLocator"

这个变量(我在一开始就声明了)将被用来给你想创建的节点类型起一个名字。

nodeTypeId = OpenMaya.MTypeId(0x87079)

这个变量(我在一开始也声明了它)将被用来给我们的节点类型赋予一个ID(标识符)。

glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()

这是它变得复杂的地方(尤其是对我来说),文档中没有提到这个命令(尽管它出现在C++的示例代码中)。正是由于这个命令,我们才能够 "绘制 "我们的定位器(使用OpenGL命令)。

glFT = glRenderer.glFunctionTable()

glFunctionTable() 方法返回一个指针到包含 Maya 处理的所有 OpenGL 指令的对象。

现在,我们真正进入了OpenGL(好吧,也许还不完全是,您将看到)

OpenGL, some informations

什么是OpenGL?(Wiki) 总结一下,就是与显卡沟通的一种方式。它涉及到一些简单的命令,比如显示点、线、三角形、四边形、纹理等。

在我们继续往下说之前,我们要先了解一下这一切是如何运作的。如果你想 "画出 "定位器,你必须知道如何使用这支笔来完成它。

由于一段代码比长篇大论更有价值,下面是如何在OpenGL中 "画 "出一条线(这是一个C语言源码。对于Maya来说,我们会发现,有两三样东西是你必须要先知道的)

glBegin(GL_LINES)
glVertex3f(0.0, -0.5, 0.0)
glVertex3f(0.0, 0.5, 0.0)
glEnd()

这种技术是 "基本(basic)"或 "即时模式(immediate mode)"技术。

glBegin(GL_LINES)

这个命令可以让我们 “进入原始绘图模式”(在三维空间中),我们把铅笔对着纸,如果你喜欢的话。参数(GL_LINES)让我们可以说下面的命令将如何解释,在这里进行内联。每隔两个顶点,我们再写一行。每一对顶点写一行。

glVertex3f(0.0, -0.5, 0.0)
glVertex3f(0.0, 0.5, 0.0)

这两条命令是将顶点 "放置 "在空间中的命令。这条命令是glVertex,数字是参数的数量(这里是三个:x、y、z),最后一个字母是数据类型(这里的 "f "表示float。)

基本上,我们在空间中放置两个顶点。考虑到我们已经把自己放到了 "直线绘制 "模式下(每个顶点对),我们只是画了一条线。

glEnd()

而这只是一种与显卡 “关闭通讯”,返回主程序的方式。

正如你所看到的,OpenGL并不是那么难的事情! 我也是这样理解的,是处理器计算每个图像中所有顶点的位置(好吧,这是老方法;还有其他的方法,尤其是静态对象,它是存储在显卡内存中,由一条指令调用的静态对象。话虽如此,但很大一部分工作是由处理器来完成的)

有了这些,你就差不多可以画出自己的定位器了。我只剩下一点要解决的问题了。

OpenGL, the Maya version!

事实上,我给大家看的代码是一个C语言程序。下面我就给大家解释一下Maya特有的几个OpenGL的一些特殊性。

出于对互操作性的考虑(我想还有其他原因),Maya有自己的OpenGL实现。因此,当我们希望在视口中绘制时,我们并不直接使用Windows或Mac的实现,而实际上是Maya自己的实现。(在C++中也可以这样做,这样就可以省去Maya的封装器,但你必须在代码中处理好互操作性。在Python中应该也可以这样做,但我还没有尝试过)。这在代码中没有什么变化,但常量(例如GL_LINES)有另一个名字。

正如 Maya API 文档中所写的那样:

The naming convention used in this file is to take the regular GL* definitions and add a “M” prefix

这个文件中使用的命名方法是: 在这个文件中使用的命名惯例是采用常规的 GL* 定义,并添加一个 "M "前缀。

翻译: “我们在所有的OpenGL常数前面加了一个’M’”。

所以Maya不会理解GL_LINES,只能理解OpenMayaRender.MGL_LINES

下面是我们如何用OpenGL在Maya中写出一条线:

glFT.glBegin(OpenMayaRender.MGL_LINES)
glFT.glVertex3f(0.0, -0.5, 0.0)
glFT.glVertex3f(0.0, 0.5, 0.0)
glFT.glEnd()

所以没有太大的区别了^^.任何一个有经验的OpenGL用户应该都可以闭着眼睛完成!

So shall we write this locator then?

不,还不行!还有很多东西要学! 在对OpenGL的小介绍之后,让我们回到我们的Python脚本上。

为了能够绘制出定位器,我们必须要派生出 "draw "函数(是的,因为Maya允许你派生函数…不是每个人都能说得这么多,嗯,XSI? ) 这个函数是用来…使用OpenGL函数绘制自定义几何图形的。

下面是 "draw "方法的基本代码。:

def draw(self, view, path, style, status):
 
	view.beginGL()
 
	view.endGL()

beginGL()和endGL()函数使我们可以(嗯,这是我的解释)告诉Maya,我们要在它们之间使用OpenGL命令。根据文档,如果没有把它们包含在正确的地方,程序将超过分配的内存,因此程序将崩溃。

我刻意将几个函数暂时忘掉,我们以后再看。

Go Pawaaaah!

所以我们现在的代码是这样的:

import sys
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaRender as OpenMayaRender
 
nodeTypeName = "myCustomLocator"
nodeTypeId = OpenMaya.MTypeId(0x87079)
 
glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
glFT = glRenderer.glFunctionTable()
 
class myNode(OpenMayaMPx.MPxLocatorNode):
	def __init__(self):
		OpenMayaMPx.MPxLocatorNode.__init__(self)
 
	def draw(self, view, path, style, status):
 
		view.beginGL()
 
		glFT.glBegin(OpenMayaRender.MGL_LINES)
		glFT.glVertex3f(0.0, -0.5, 0.0)
		glFT.glVertex3f(0.0, 0.5, 0.0)
		glFT.glEnd()
 
		view.endGL()
 
 
def nodeCreator():
	return OpenMayaMPx.asMPxPtr(myNode())
 
def nodeInitializer():
	return OpenMaya.MStatus.kSuccess
 
def initializePlugin(obj):
	plugin = OpenMayaMPx.MFnPlugin(obj)
	try:
		plugin.registerNode(nodeTypeName, nodeTypeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kLocatorNode)
	except:
		sys.stderr.write( "Failed to register node: %s" % nodeTypeName)
 
def uninitializePlugin(obj):
	plugin = OpenMayaMPx.MFnPlugin(obj)
	try:
		plugin.deregisterNode(nodeTypeId)
	except:
		sys.stderr.write( "Failed to deregister node: %s" % nodeTypeName)

You can download it here:

CustomLocatorNode001.7z

Load it (as a plug-in), and enter:

createNode myCustomLocator;

我们刚刚做了第一个定位器! 有了这个,我们已经可以做一些事情了…如果你给它点random,就可以得到这个:

import random

glFT.glBegin(OpenMayaRender.MGL_LINES)
glFT.glVertex3f(random.uniform(-0.5, 0.5), random.uniform(-0.5, 0.5), random.uniform(-0.5, 0.5))
glFT.glVertex3f(random.uniform(-0.5, 0.5), random.uniform(-0.5, 0.5), random.uniform(-0.5, 0.5))
glFT.glEnd()

Le code: CustomLocatorNode002.7z

You’ll have a wicked locator! ^^

Right, it’s all very well making lines but that’s not all there is to it… I suggest we do… a quad! !

glFT.glBegin(OpenMayaRender.MGL_LINES)
glFT.glVertex3f(0.0, -0.5, 0.0)
glFT.glVertex3f(0.0, 0.5, 0.0)
glFT.glEnd()
 
glFT.glBegin(OpenMayaRender.MGL_QUADS)
glFT.glVertex3f(-0.5, 0.0, -0.5)
glFT.glVertex3f(0.5, 0.0, -0.5)
glFT.glVertex3f(0.5, 0.0, 0.5)
glFT.glVertex3f(-0.5, 0.0, 0.5)
glFT.glEnd()

The code: CustomLocatorNode003.7z

Paf le code!

Paf le locator!

好吧,虽然有点浮夸,但我们要改变一下! 我们要改变颜色,增加透明度。

首先是颜色。默认情况下,Maya使用的是界面颜色(未选中时为蓝色,选中时为绿色,"模板化 "时为粉色等),要改变颜色,只需添加命令glColor3f()

glFT.glBegin(OpenMayaRender.MGL_LINES)
glFT.glVertex3f(0.0, -0.5, 0.0)
glFT.glVertex3f(0.0, 0.5, 0.0)
glFT.glEnd()
 
glFT.glColor3f(1, 0, 0)	#on change la couleur
 
glFT.glBegin(OpenMayaRender.MGL_QUADS)
glFT.glVertex3f(-0.5, 0.0, -0.5)
glFT.glVertex3f(0.5, 0.0, -0.5)
glFT.glVertex3f(0.5, 0.0, 0.5)
glFT.glVertex3f(-0.5, 0.0, 0.5)
glFT.glEnd()

The code: CustomLocatorNode004.7z

Re-Paf le code!

Re-Paf le locator!

还是一样的浮夸,呵呵。你会注意到,只有线条的颜色会随着选择状态而改变。为了让我们的定位器更 "轻盈 "一些,我们要给我们的四边形添加透明度。

The Alpha

我们要 "启用 "OpenGL的一个功能。GL_BLEND (别忘了在最后禁用它):

#We activate feature
glFT.glEnable(OpenMayaRender.MGL_BLEND)
 
glFT.glBegin(OpenMayaRender.MGL_LINES)
glFT.glVertex3f(0.0, -0.5, 0.0)
glFT.glVertex3f(0.0, 0.5, 0.0)
glFT.glEnd()
 
glFT.glColor3f(1, 0, 0)	#Change color
 
glFT.glBegin(OpenMayaRender.MGL_QUADS)
glFT.glVertex3f(-0.5, 0.0, -0.5)
glFT.glVertex3f(0.5, 0.0, -0.5)
glFT.glVertex3f(0.5, 0.0, 0.5)
glFT.glVertex3f(-0.5, 0.0, 0.5)
glFT.glEnd()
 
#Don't forget to unactivate in the end!
glFT.glDisable(OpenMayaRender.MGL_BLEND)

当这个功能被启用后,OpenGL会根据最终的系数(Alpha)将给定的颜色与生成的颜色(背景)混合在一起。所以我们必须在颜色中添加最后一个组件 :

glFT.glEnable(OpenMayaRender.MGL_BLEND)
 
glFT.glBegin(OpenMayaRender.MGL_LINES)
glFT.glVertex3f(0.0, -0.5, 0.0)
glFT.glVertex3f(0.0, 0.5, 0.0)
glFT.glEnd()
 
glFT.glColor4f(1, 0, 0, 0.5)	#Change color and add alpha
 
glFT.glBegin(OpenMayaRender.MGL_QUADS)
glFT.glVertex3f(-0.5, 0.0, -0.5)
glFT.glVertex3f(0.5, 0.0, -0.5)
glFT.glVertex3f(0.5, 0.0, 0.5)
glFT.glVertex3f(-0.5, 0.0, 0.5)
glFT.glEnd()
 
glFT.glDisable(OpenMayaRender.MGL_BLEND)

Le code: CustomLocatorNode005.7z

我们来到了最后一部分:选择状态.

Selection

事实上,我们可以根据定位器的选择状态来改变我们的定位器的各个方面。在这里,我们要改变… 的颜色!(很有创意… -_-)

首先,一个 "绘制 "对象有几种 “状态”(列在M3dView:::DisplayStatus中)。下面我给大家举出两个主要的状态,也就是我们要使用的状态。

  • kActive ->活动的(选定的)对象。(小心点,这个很棘手!)
  • kLead -> 最后一个被选中的对象。
  • kDormant -> 非活动对象。

所以,让我们来解释一下其中的细微差别:

在Maya中,当你选择了一个对象,它就变成了绿色。而当你在选择中添加另一个对象时,第二个对象会变成绿色,而前一个对象会变成白色。事实上,绿色对象是 “in DisplayStatus” kLead,而白色对象是在kActive中。这里有一张图片来解释(或者提醒你?)这个原理。

所以我们要把颜色作为选择状态的函数来改变。

如上图所示,派生函数是draw,我们用来知道定位器的状态的参数是 “status”。而没有什么比使用这一切更简单了:

import maya.OpenMayaUI as OpenMayaUI # on top

if status == OpenMayaUI.M3dView.kLead:
	glFT.glColor4f(1, 0, 0, 0.3)	#rouge
if status == OpenMayaUI.M3dView.kActive:
	glFT.glColor4f(1, 1, 0, 0.4)	#jaune
if status == OpenMayaUI.M3dView.kDormant:
	glFT.glColor4f(1, 0, 1, 0.5)	#mauve

CustomLocatorNode006.7z

好吧,虽然不是那么漂亮,但很好用…

我希望这篇教程能让你对Python Maya API的几个方面有所了解… 不过请注意,我没有选择最简单的东西开始(如果你对API一无所知的话…否则,我觉得很简单),但有了这个,你已经有了第一个工具包,可以让你迈出第一步… !

发布了7 篇原创文章 · 获赞 2 · 访问量 1495

猜你喜欢

转载自blog.csdn.net/u013148608/article/details/105694909