mayaAPI(10)-shape(上)

——————>

人总是会被很多事物影响,在周围有太多太多与自己毫无关系的世界在左右,以至于使自己找一些借口去这些世界浪费时间。

浪费时间原因有二,一个是无聊,二个是累了。其实人本身是比较难以真正疲劳的,大多数感觉疲劳的原因是自己做某件事情花费了太多精力,以至于自己身体出现了一些变化而不知。很多时候精神上的疲劳都是由于忽略了身体的变化导致,所以最好的休息方式就是不让自己想任何事情,或者只专注于自己身体内在的信息。

所谓无聊就是自己精神力太充沛了,需要找个专注的事情去做,以至于会去玩游戏,看电影,逛街之类 ,精神的空虚就是要找自己感兴趣的事情去做,就算每天仅仅完成一点,都是可以的。

——————>

要写一个shape可是一个比较复杂的工程了。

以前我们都是用function set来创建shape node,这是把大部分工作都交给maya去完成了。但是shape毕竟是maya的很基础的部分,稍微研究一下也是很有好处的。

——————>

先要知道我们写这个shape node需要的母类:

1.MPxComponentShape

Component helper class for surface shapes.

MPxComponentShape allows the implementation of new user defined shapes using components. User defined shapes are dependency nodes (and DAG nodes) which contain overridable drawing, selection, and component methods.

2.MPxSurfaceShapeUI

drawing and selection for user defined shapes

The base class for the UI portion of all user defined shapes.

3.MPxGeometryIterator

This is the base class for user defined geometry iterators. Geometry iterator allow iterating over components in geometry in a geometry independent manner. This base class defines the interface to be used by maya when a generic component iteration is required.

——————>

然后看看这三个类的作用以及如何配合使用。当然,我现在先勾勒一个大致的框架:

我们要做一个shape,它需要显示,这个需要在屏幕里面画一些图形,图形的生成和刷新方面一定有类来负责

当画出来之后,我们需要操作,这个图形要根据我们的manipulator来做出不一样的显示,manipulator与shape node之间如何交互?这是一个方面。

我们的shape node 是由vertex组成的,多个vertex组成了face和edge,这些在maya里面都是内部object,说得形象一点,是component node,我们要如何得到这些component node?并且manipulator如何得到以及操控这些manipulator的数据呢?

好了,带着这些问题,来看看maya给的例子!

——————>MPxSurfaceShapeUI

看到这个母类的说明,我觉得应该从这里开始

API上说:

Drawing is a two step process. In the first, the geometry and materials are evaluated and all of the information necessary for drawing is placed onto a queue.

这句话对应的类中的方法是:

// Puts draw request on the draw queue
//
virtual void getDrawRequests( const MDrawInfo & info,
bool objectAndActiveOnly,
MDrawRequestQueue & requests );

[in] info the drawing state information
[in] objectAndActiveOnly This parameter is used to determine if draw requests for components need to be supplied. If false, some or all components are active and draw requests must be built for all components.
[in] queue the drawing queue to place the draw request on

知道了这个之后,就要知道,到底那个类或者是哪个方法里面会来调用这个方法!

Drawing occurs whenever the camera changes or the view has to be refreshed. When this happens, the MPxSurfaceShapeUI::getDrawRequest function is called. This is Maya’s way of asking the shape what it needs to draw. Inside this function you should query the drawing state, using MDrawInfo, and then construct a draw request (MDrawRequest) to place on the queue.

这里说,maya会自动调用这个getDrawRequests方法,那么,maya是如何知道这个方法的呢,看看我们如何将我们写的派生类让maya知道!

看着maya给出的说明:

Registering shapes with Maya is similar to registering DG nodes. The only difference is that shapes have a separate UI class that must be registered with the shape node.

MStatus initializePlugin( MObject obj )
{
	MFnPlugin plugin( obj, PLUGIN_COMPANY, "5.0", "Any");
	MStatus stat = plugin.registerShape( "apiSimpleShape", apiSimpleShape::id,
								   &apiSimpleShape::creator,
								   &apiSimpleShape::initialize,
								   &apiSimpleShapeUI::creator  );
	if ( ! stat ) {
		cerr << "Failed to register shape\n";
	}

	return stat;
}

在我们的mll入口方法中,我们要如上所示来填写.shape node 和DG node有一点不同,虽然shape node是DG node的一种,但是它还涉及到geometry data,这个geometry data是可以用来渲染以及很多种用途的,它不是普通意义上用openGL画出来的图形。所以,在这里分开来,写一个UI的派生类也是非常好的任务分工。

UI的creator方法会返回我们派生类的实例,所以maya就会根据这个实例找getDrawRequests的函数地址。

好了,继续我们的绘画,我们要知道getDrawRequests是如何绘画才能制作出geometry data呢?

看这最开始的几行代码:
// Get the data necessary to draw the shape
//
MDrawData data;
apiSimpleShape* shape = (apiSimpleShape*) surfaceShape();
MVectorArray* geomPtr = shape->getControlPoints();

// This call creates a prototype draw request that we can fill
// in and then add to the draw queue.
//
MDrawRequest request = info.getPrototype( *this );

// Stuff our data into the draw request, it’ll be used when the drawing
// actually happens
getDrawData( geomPtr, data );

request.setDrawData( data );
….
queue.add( request );
第一行是准备个数据结构来放geometry data。
第二,三行告诉我们,这个geometry data是从apiSimpleShape得到的,API里面给我们这个解释:

MPxSurfaceShape * MPxSurfaceShapeUI::surfaceShape ( ) const

Returns the non-ui shape associated with current instance of this class.
Returns:
The non ui shape object
这里的shape object就是我们要写的shape node部分。
shape node有一个方法getControlPoints()
这个方法让我们可以的得到CV点,CV点在maya里面是这样做的:
Shapes which are formed and manipulated by the position of control vertices (or CVs) are called control point shapes. The control points are attributes on the shape which are usually arrays of points.
它就是作为shape node 的属性,每一个cv点都是MVector(MPoint)结构的,也就是3个double(vector)。在maya里面是用array attribute node来作为数据接口。
mel里面就是:

// // Now add some CVs, one
// string $attr = $node + ".controlPoints[0]";
// setAttr $attr 2 2 2;

第四行我们又要遇到两个陌生的类MDrawInfo和MDrawRequest。先看看getPrototype的说明:
MDrawRequest MDrawInfo::getPrototype ( const MPxSurfaceShapeUI & drawHandler ) const

This method creates a draw request based on the current draw state.

The draw request is placed onto maya’s drawing queue (MDrawRequestQueue) where it can be processed in turn. The drawHandler argument is the shape that will be doing the drawing which is the object calling this function.

我们从这里只能知道,request实例是基于info与UI类的绑定,从而得到的。
This getPrototype method is used to construct a draw request object based on the current draw state for the object.
我们从info得到的信息包括:

Information automatically set by MDrawInfo::getPrototype :

  • path path to object to be drawn
  • view view to draw in
  • matrix world matrix for object
  • display appearance how object should be drawn
  • display status active, dormant etc.

这些信息maya都会自动的传给我们,所以,在代码中我们都必须按这样的格式来写!
第5,6部就是把MVectorArray转化成MDrawData数据结构,用的是getDrawData,然后把数据存到request实例中。

void MPxSurfaceShapeUI::getDrawData  ( void *  geom,
  MDrawData &  data
 )    

Sets up draw data for the shape. The draw data is meant to be a light weight class which can be used to pass geometry data through draw requests.

最后,就把request加到queue中就是了。

——————>

getDrawRequests并不是真正执行绘画的部分,它是maya环境->shape node 绘画之间的一个数据类型转换器,它把maya给shape node的信息转换成绘画所需要的数据结构。

所以,我们的MDrawRequest可以算是提供数据结构模板。

实例利用这个模板里的数据来画画,是在draw方法里面,maya自然会去调用它,并把request,以及view当参数传过去。

MDrawData data = request.drawData();
MVectorArray * geom = (MVectorArray*)data.geometry();

我们从request里面得到了drawData,这个data里面包括了maya环境信息:dag path,3d view,world 矩阵,以及一些显示状态。

然后就要调用openGL方法进行绘画。在绘画过程中还要考虑是否有activeComponent

在draw这里,它只是对于cv点的位置进行绘画,至于材质,颜色等等信息,都包含在request中,不再做修改,maya会取出queue里面的request里的材质,颜色数据,就像在getDrawRequests里面的这几行:

<pre>

MDagPath path = info.multiPath(); // path to your dag object

M3dView view = info.view();; // view to draw to

MMaterial material = MPxSurfaceShapeUI::material( path );

</pre>

所以说,draw只是画vertex(vertex data group也就是geometry data)

component的存在要影响绘画,因为有component的存在的话,我们就要对shape node重绘。(就是我们点选择或者其他选择模式下,shape node 的表现方式不一样)

component到底是什么呢?其实很简单,它是个索引类,我们对属性的操作其实一直是通过MPlug的,cv是属性,自然也不例外了。

只是maya在这里没有让我们麻烦的使用MPlug,毕竟cv的数据结构是固定的,每个cv都是MVector类型,没有其他,所以就不需要wrapper来再包裹一层了。

那么,我们需要什么来判断是否cv点被manipulator选中了呢?component在这里就做这个工作,我们每选中一个点,它就把这个点的序号加进去。

那么,这个索引库有什么用?那要在select方法里面用了,maya自然有方法支持这样的数据类型

——————>

component实际上就是索引库,只是maya很无良的在这里取了个这样的名字,component有零件之意,但是maya却不是给的实体,而是放的是索引。。一开始我以为所有的CV数据都是放在component里面的,结果呢。。他只是所索引库而不是database。。无良。。

(数据类型能告诉他骨子里卖什么药,算法可以隐藏,但是数据结构就能告诉人这个程序的脉络,而对这个数据结构实例的命名都是浮云。)

不过,我想关于component的使用涉及到selectionList的又一种应用,还是放到下一篇吧。

不过,应该知道的是,maya做了个简单说明:

they can be selected and manipulated, by associating the control point attributes with a component.

Components are objects (MObject’s) which contain two pieces of information, the type of component and the index values or range. For example, a mesh vertex component. This component represents the vertex (or “pnts”) attribute of a mesh shape

component是select的时候才有值,它存储的是索引,并且这个索引库有多种类型,也就是几重索引,就像数据库里面的,有时候primariy key有多个。

观念

1.人生的意义(比如抽象一点的:做自己)。
2.社会影响(天下没有免费的午餐)
3.气(外部影响磁场,比如天气,情爱等等)
4.人的内驱力(本能与欲望)
——————>人生意义
人生的意义,很多人的人生意义不同,但是大多数人的人生意义都是为自己做XXXX。
除非是小说里面写了比较极端的情况,某些配角的人生意义变成了为了主角XXXX。
所以,这里”做自己”其实是比较模糊的一个概念。
我们可以这样说:
“我想要[形容词]做[宾语]”
或者更简单的人生意义就是:
“我想要得到[宾语]”
前一句比后一句说得更好,因为咱们有第二条,”天下没有免费的午餐”,想得到的东西,都得有所付出!
所谓梦想就是归于第一类。
其实学校它所教育的应该是包括1,2,4。 但是中国教育在这方面做得并不好,人生意义不教,压抑内驱力以及对气的作用完全忽略!
——————>社会影响
在社会里面生存,我们就有一种价值观。
社会里面无法就是为了生活,生活的最低限是生存,但是生活没有标准的最高值,它在每个人的心中有不同的最高值以及适合值,这里就有一个关于度量的问题。
这个度就是价值。所谓价值观,就是人对于这个度的看法,人们觉得某个度处于哪个区间里面,那么,人就产生了不同的看法。
也就是:
对某事物的价值看法=function(事物A,生活现状,度量标准);
或者是:
价值观=function(所有事物,生活现状,度量标准);
这个function就是代表思想的过程。价值观以及看法就是度,他们是同义词。
就像有些人考试觉得60分就OK,有些人觉得80分还不够,这就是对价值看法。
这里有个非常智慧的古老谏言:天下没有免费的午餐。
这句话就告诉了我们一个关于价值产生的function,这个function必须要有参数,参数为NULL的话,返回值为NULL
收获=function(付出)
但是付出和收获的关系,并不是正比。但至少在某一个范围内,是线性相关。。
——————>气
气有两种,人的气与自然的气。人的气当然也有两种,人自己的气与其他人的气。
说明:
人自身是有电磁场的,这个电磁场会随着时间的运转而出现波峰波谷,并且它的磁极中心会在人的身体上移动。
人自身的电磁场会被外在的电磁场影响,比如阳光,比如地形,比如场合,这就是风水影响,也就是自然的气。
人的磁场会被其他人影响,这产生的结果是爱情,亲情,友情,同情心等等。
当然了,人的磁场被影响的还有一个很简单例子就是疾病,这可以算是被外部磁场直接作用于人的身体之内,自然产生了磁场影响(摄取了人类磁场的能量或者是扰乱)
——————>内驱力
这个内驱力其实应该是人自身的磁场产生的,从而每个人都所具有的:被吸引。(应该是波越相似就吸引力越大吧。。可是人体的波也是个变动的。。所以,内驱力也会变动)
这种被吸引就造成了我们一种动力,一种拥有某种事物的动力!情感也是这样,情感也是一种对方产生的吸引力。

所谓,本能,欲望,情感,其实都是外界磁场交互所产生的。虽然能这样概括,但是世界上变化太多,都不知道哪一个时刻会产生什么,这里只是做个概括罢了。

人生意义是因为内驱力的原因才产生,内驱力很繁杂,因为外界的诱惑太多,但是人要有所成就,就必须把力往一处使!

所以,就必须要定义大致明确的目标,排除一些无关紧要的内驱力。

——————>

总而言之,这里面最关键的因素是气,但是气(这是世界观)的表现太过复杂,对于人而言,主要的气的影响就是价值观:内驱力(个人价值观)和社会影响(社会价值观)。价值观无论是个人还是社会,都是分三类,目的,情感和普通价值。对于个人而言,目的就是人生观,对于社会而言,目的就是治国纲领之类。

嗯。。按照最后的一个总结思路,那么可以对这4条精练的写下:

1.人生观

2.社会普通价值观

3.世界观

4.个人情感观

mayaAPI(9)-manipulator

API实在太复杂。。
——————>manipulator就是其中很让人头疼的一个
是不是跳过它呢??不过他也涉及到了locatorNode。
loctorNode它是一个DG node,那么自然就有许许多多的属性了。它的这些shape都已经定义好了,它内部是有实现的,就像我们之前做了MPxNode一样,它是有实现很多的input和ouput属性的,但是呢,它只是给这些node搭建好了架子,compute方法是空的,draw方法也是空的,当然initialize方法也是空的。
——————>
其实一开始我也没有想到这么多,而是因为maya自带的一个makeIdentity命令弄得我比较烦躁。从而让我发现maya给我的example过于简单了。
这个footPrint node虽然样子上是个locator,它在draw里面画出了shape。
但是它少了很重要的一个方面,就是没有重画shape的功能。。
因为makeIdentity方法就是让shape进行重画,所以transform节点的属性可以归0,这些移动值放到shape节点的属性里面去(也就是localPosition)。
在draw方法里面,需要取到localPositon的值,然后位移,重画,在原方法中并没有这样一步。
——————>
提到了position就不得不说到Matrix。
position是以自己为坐标系的移动,它是Matrix的一个组成部分(transform的所有属性(乘法)一起组成这个Matrix)
那么,worldMatrix是什么呢。它是DAG图上,从根node到自己的所有的Matrix的乘积。
我们把这个路径上的Matrix可以分为两类,一类是自己Matrix,一类是parentMatrix。
parentMatrix可以有多个,因为我们有些父node是用我们自己来实例化的(就像实例化shape node),这样的情况下,DAG path就会出现分支,我们的父node就会有多个,这时,maya就给这些父实例加上index,我们就知道是哪个parent了。
worldMatrix[0]=Matrix乘以parentMatrix[0]就是这个道理!
——————>
至于inverseMatrix,下面这个网站描述了一下。

http://mathworld.wolfram.com/MatrixInverse.html

所谓的逆矩阵就是:将矩阵的每一个元素替换成它所对应的代数余子式,然后将这个新矩阵除以矩阵的行列式。这样我们得到的结果矩阵就是逆矩阵了。呼。。好多年前的知识哦,幸亏我的线性代数课本没卖掉。。

——————>

bounding box

这个东西想知道是做什么的很简单,下载一本openGL 红宝书翻翻看就是了。

一般来说,一个物体的bounding box就是以它的最小point和最大point组成一个对角线来做个cube。

这个cube可以决定物体显示与碰撞,是比较剩资源的解决方案!

——————>

差不多可以去搞定manipulator了。。

——————>manipulator

A manipulator is a node that draws itself using 3D graphical elements that respond to user events. Manipulators translate the events into values which are used to modify attribute values of other nodes in a scene. The attribute values are modified directly by the manipulator and not through the standard plug and connection mechanism used by other dependency graph nodes.

我们常见的DG node之间,是用属性链接的方式(attribute connection)来传递数据的,这样的传递方式也就形成了network nodes

maya还提供了一种方式,让我们直接操控DG node的attributes(plugs),这个就是manipulator。

manipulator是需要一种context(自定义context或者Show Manipulator Tool)。

数据传输是这样的,manipulator->plug,然后plug->node

(嗯。plug其实就是public set方法。他的多功能性在于,我们在maya里面的操作,修改了plug,plug不仅仅修改我们这写的类中的这个属性,还会根据这个修改去看是否有output MObject与input MObject相关,然后置为dirty。MObject和node是同义!)

plug可以被manipulator所修改:

MPlug sizePlug = nodeFn.findPlug("size", &stat);
    if (MStatus::kFailure != stat) {
	    distanceManipFn.connectToDistancePlug(sizePlug);
		.....
	}

这里一句connectToDistancePlug的意思就是,我们的distanceManip要影响哪一个数据结构(distance plug)
这个plug新建的时候是这样定义的:
size = unitFn.create(“size”, “sz”, MFnUnitAttribute::kDistance);
我们需要kDistance类型的数据。

——————>

在我们的例子中,我们是修改locator,locator有一个draw方法,这个draw在工作区刷新的时候会默认调用,这个是肯定的。

manipulator的操作也会引起工作区刷新,所以这个draw方法也会被调用。所以,我们在locator里面存下size 这个node。在draw方法里面写着:

MObject thisNode = thisMObject();
MPlug plug(thisNode, size);
MDistance sizeVal;
plug.getValue(sizeVal);
float multiplier = (float) sizeVal.asCentimeters();

在maya里面,我们的node都是存为MObject类型(attribute就这样存,而DAG不可以这样存,因为DAG需要静态数据,而且maya会智能的对DAG的MObject进行一些修改,对于maya的数据结构,也不要存引用(因为我们的function set需要的都是对象而不是引用,存引用是搞不定的。。))
所以,我们这里size是动态存储的,它里面的值会随着我们的一些特定操作而变化!!(我们不能存plug类型的size,那样就静态了,maya不知道如何修改的)
从MObject->plug有2步:

MObject thisNode = thisMObject();
MPlug plug(thisNode, size);

由于我们这个plug里面的数据时kDistance类型的(maya内部数据类型),我们又得写2步:

MDistance sizeVal;
plug.getValue(sizeVal);

distance是基础数据类型的wrapper,我们要转化下;

float multiplier = (float) sizeVal.asCentimeters();

这样我们就得到float基础类型数据,可以来使用了。。(maya就是这样,它的wrapper太多了)
——————>
要让manipulator操纵一个node有几个步骤要写:
首先是命名上,它是这个node的class名后面加上Manip
其次是这个node的initialize方法里面要加上
然后呢,我们就要写回调函数了!回调函数的发生时间分别在plug改变和manip item改变之时。
代码是这样写的:

		unsigned startPointIndex = distanceManipFn.startPointIndex();
	    addPlugToManipConversionCallback(startPointIndex,
										 (plugToManipConversionCallback)
										 &footPrintLocatorManip::startPointCallback);

但是呢,maya也提供给我们一种one-to-one的connection方案,比较让人奇怪的是,这两种方案可以并存。。嗯。。应该是只要两种方案不修改同一个对象应该就没问题吧。。
使我有些不理解的是API上写了这么一段:
The conversion between converterManipValues and converterPlugValues are performed through conversion callback methods, except when there is a one-to-one connection between a converterManipValue and a converterPlugValue.
例子的写法是两种方式可以并存的。。嗯。话说这段话也没说两个都用上会有神马后果哦。。
我们可以不像例子中写one-to-one connection的。我们要写manipToPlug这个conversion需要用到的API就发现一点问题:

unsigned int MFnDistanceManip::distanceIndex  ( MStatus *  ReturnStatus = NULL   )  const 

Returns the index of the distance. The data type corresponding to this index is a double.

得到这个index之后,我们用类似下面这样的方法:

MStatus  getConverterManipValue (unsigned int manipIndex, unsigned int &value)
MStatus  getConverterPlugValue (unsigned int plugIndex, double &value)

就可以得到manipValue和PlugValue
在这个Container中,所有的value item都已经编号了,都有index,我们要用特定的方法取出value。
——————>
conversion方法可以有两种写法,一种是继承Container里面默认的conversion方法:

virtual MManipData  plugToManipConversion (unsigned int manipIndex)
virtual MManipData  manipToPlugConversion (unsigned int manipIndex)
void  addPlugToManipConversion (unsigned int manipIndex)
unsigned int  addManipToPlugConversion (MPlug &plug)

还有一种就是callback方法,callback就是我们回调函数的地址:

void  addPlugToManipConversionCallback (unsigned int manipIndex, plugToManipConversionCallback callback)
  NO SCRIPT SUPPORT. 

unsigned int  addManipToPlugConversionCallback (MPlug &plug, manipToPlugConversionCallback callback)
  NO SCRIPT SUPPORT.  

manipIndex有这样的解释:
the index of the converterManipValue for which the callback will be registered
第一种方案中,我们可以写多add来监听不同的index或者plug,但是conversion方法只有一个,这个也就是统一来处理。
第二种方案就是,我们给每个index架设监听方法,这样conversion方法就是多个了!
回调函数其实除了名字不一样,其他都要和固定方法一样,返回对象依然是MManipData。
C++里面的写法是要做强制类型转换,并且类中的成员函数名不是地址,而是作为类似变量的存在:
(plugToManipConversionCallback) &回调方法名
——————>
callback方法需要对应的index,这个index是从base manipulator对象中得到的,再引用一下代码:

		unsigned startPointIndex = distanceManipFn.startPointIndex();
	    addPlugToManipConversionCallback(startPointIndex,
										 (plugToManipConversionCallback)
										 &footPrintLocatorManip::startPointCallback);

我们这里callback方法的返回值,就是将这个值赋值给index所对应的startPoint,它需要的数据类型是3dobule:

        MFnNumericData numData;
	MObject numDataObj = numData.create(MFnNumericData::k3Double);
	MVector vec = nodeTranslation();
	numData.setData(vec.x, vec.y, vec.z);
	return MManipData(numDataObj);

我们用data function set来创建数据对象,它的值是我们manipulator所操纵的dag node 的translation 值这个值写了个单独方法来求得:

MVector footPrintLocatorManip::nodeTranslation() const
{
	MFnDagNode dagFn(fNodePath);
	MDagPath path;
	dagFn.getPath(path);
	path.pop();  // pop from the shape to the transform
	MFnTransform transformFn(path);
	return transformFn.translation(MSpace::kWorld);
}

我们的Dag node如何得到呢?
那就是根据connectToDependNode方法传来的参数:
the node to which the manipulator should be connected
——————>
这里的监听方法有点复杂。。它封装得够简洁。。用回调函数的返回值来做输出。。

mayaAPI(8)-plug

maya里面有这样一个类,名字叫MPlug,它和plugin无关。这个类是用attribute来实例化的,也就是attribute的一个封装类,里面有很多方法。

Plugs are constructs which contain the data for a given attribute. All data access for the attribute is done through the plug. The attribute itself only defines the data type and name for the attribute. Plugs also are the ports for making connections between nodes.

具体如何使用要慢慢来学。

——————>

这里有一段告诉我们如何给属性排序的:

By default, Maya will automatically arrange the attributes of a node in the attribute editor. If you desire a special arrangement of the attributes of your node, you can write an attribute editor template for the node. This is a MEL file on your MAYA_SCRIPT_PATH whose name is of the form AE{nodeName}Template.mel that contains a MEL procedure with the name AE{nodeName}Template. This procedure contains editorTemplate commands that instruct the attribute editor how to alter the default layout for the attributes in the node.

——————>

one can define an attribute whose data type itself is an array. Such an attribute would contain an array plug whose element plugs each contained a whole array. Each array could be independently accessed. As noted above, connections between array plugs are not recommended.

我们定义的每个attribute都可以是MFnData里面的数据类型,这个数据类型里面也包括array。

这是attribute这个指针对应的数据结构。maya里面也把这样的指针做成了指针数组。所以,我们也就有了array attribute这样的东西。

maya的API在这里绕来绕去说得很是麻烦。。其实就是attribute就是指向某个数据的指针。plug的话,是对attribute对应的数据的操纵,所以,plug和attribute也几乎是一个意思。所以mayaAPI在这里解释得也比较乱。

一个attribute是对应一个plug,一个array attribute 也是对应一个array plug。

后者需要特定的方法:

 MPlug tweakPlug = depNodeFn.findPlug( "pnts" );
        if( !tweakPlug.isNull() )
        {
                // ASSERT: tweakPlug should be an array plug!
                //
                MAssert( (tweakPlug.isArray()),
                                 "tweakPlug.isArray() -- tweakPlug is not an array plug" );

                MPlug tweak;
                MFloatVector tweakData;
                int i;
                int numElements = tweakPlug.numElements();

                for( i = 0; i < numElements; i++ )
                {
                        tweak = tweakPlug.elementByPhysicalIndex( i, &status );
                        if( status == MS::kSuccess && !tweak.isNull() )
                        {
                                getFloat3PlugValue( tweak, tweakData );
                                if( 0 != tweakData.x ||
                                        0 != tweakData.y ||
                                        0 != tweakData.z )
                                {
                                        fHasTweaks = true;
                                        break;
                                }
                        }
                }
        }

上面的例子就用特定的方法来分解array plug。

最后得到的element就是普通的plug。

我们可以都可以用setValue和getValue来搞定他们。

实际操作还是需要看例子。

——————>Compound attribute

这个属性它就是算个struct了,maya这里说是collection of attributes。

我们不知道struct的声明是没法把它显示出来的。。所以maya这里所,它没有特定的data type。

也许,compound和struct一样的意思。。只是maya中习惯叫compount,而在计算机语言里面用struct。。

Compound attributes are created using the MFnCompoundAttribute class. This class contains methods for specifying the child attributes which will be contained in the compound attribute. Refer to the reference page of MFnCompoundAttribute for more information.

具体还是要看例子。。Compound属性里面包含的叫做child attribute,child attribute也自然可以是其他类型的attribute,和结构体是一样的。

上面写了个这个array attribute,就是指针数组的意思。结构体里面指针数组是很常见的,就和普通数组一样。或许啥时候maya还会把union加上去,这样不就更完整了。。

——————>

我们很多时候会自定义属性,这些属性在maya里面叫动态属性,dynamic attribute:

Dynamic attributes are used to attach blind data to a node. Every node initially has a set of attributes defined. You may, however, want to add new attributes to either a single node or to all nodes of a given type. These attributes are called dynamic attributes.

A dynamic attribute is treated much like any other attribute. The main difference is that someone is responsible for allocating and deallocating it since it will not be statically created.

它肯定也是在struct里面放个指针。不过这个指针所指向的内存,得我们自己来收拾了吧。。

在maya里面,attribute都是静态的,所以应该有张表才是。。可是我们有看到除了rendering attributes之外的attribute 表。。

MFnNumericAttribute fnAttr;
const MString fullName( “blindData” );
const MString briefName( “bd” );
double attrDefault = 99;
MObject newAttr = fnAttr.create( fullName, briefName, MFnNumericData::kShort,
 attrDefault, &stat );

stat = fnDN.addAttribute(
 newAttr,MFnDependencyNode::kLocalDynamicAttr);
if ( MS::kSuccess != stat ) {
 cerr << “Error adding dynamic attribute” << endl;
}

——————>

看完这几篇API文章,还是觉得不够满意。。

刚读完loft这个example,配合关于NurbsSurface的文档,似乎又多知道了一些。。

关于NurbsSurface,它需要control vertix。但是光有这个还是不够的,它需要一种方式来知道如何排列他们。

这个时候knot以及degree就是作为排列的标准。

在例子中,我们做的是NurbsSurface与curve的cv贴合,所以knot需要符合cv的度数,它的knot需要在头尾都乘以degree

knot以0为开始,结尾是以spans-degree为结尾。所以头上有3个点,尾巴3个点,中间是1~(spans-degree)

为了符合一条曲线,那么应该是有一个方向的knot是这样的,另外一个方向的knot就不定了。

——————>

MFnAttributes下面有非常多的派生类,其中,对于普通属性比如说float,boolean,short之类,我们都是用NumericAttribute来创建attribute node。

比较复杂一点的就是这个例子中,我们用MFnTypedAttribute来创建,API文档里并没有说到底这个子类能创建几种attribute node,难道要我去慢慢猜么。。。

在真正的C++实现里面,plug和attribute node是有分开职能的,它不想API文档里面解释的那么模糊,在C++里面,attribute node是用Function Set来搞定的,里面用create方法来创造attribute node,这样DG node就可以有方法来把attribute 加进去(奇怪的是没有减少 attribute node 的方法,什么样的数据结构不适合减少呢?那就是类似线性数组了,呵呵)

当加进去attribute之后,又因为attribute node里面包含的数据结构非常复杂,我们需要一个单独的类来handle他们,这个类就是MPlug。

他负责搞定所有的attribute node,有什么需要就可以在MPlug类里面来找!所以说,MPlug就是attribute node 的母类。在简单attribute的时候,母类和子类是同一个对象,就像我们这几个简单的例子里写的:

if ( plug == outputSurface )	// loft inputCurve into surface
	{
		MDataHandle inputData = data.inputValue( inputCurve, &stat );
...
}

plug node 和attribute node同属于MObject,这里maya会自动判断类型。这里之所以plug 会等于output attribute,而不是input attribute,也就是maya这里做了一个逻辑上的简化处理,因为我们input attribute node的值被修改之后,就被marked dirty了,然后maya找到被input node 所affects的output node,然后mark output node dirty。其实这些node都是位于plug node(plug也就是被这些node来实例化的),这里的”==”方法返回的也就应该是node实例,maya应该重载这个operator的时候,里面也有一些判断语句,所以,根据plug node中state的不同,operator所返回的结果应该就有些不同。

——————>

还有一点不同的是,我现在终于知道了一个完整的DG->DAG的创建过程。

因为我们用到了data node

		MFnNurbsSurfaceData dataCreator;
		MObject newSurfData = dataCreator.create( &stat );

我们这里创建的data,是在maya界面里面看不到的,它只是数据结构!!(我喜欢底层)
那么,我们实例花了这个MFnNurbsSurfaceData,并且他的create方法应该是涉及到内存,排序,线程等一些操作。
那么,我们就可以放数据进去了。
Function Set里面的方法是可以把结果放在data里面的,这样就不会创建transform node 和shape node了,呵呵。

	MObject surf = surfFn.create(
		cvs, ku, kv, 3, 3,
		MFnNurbsSurface::kOpen, MFnNurbsSurface::kOpen,
		false, newSurfData, &stat );

例子中最后是放到这个data里面的。
最后,我们该如何用这个data?我们需要把它放到output attribute node里面(这个node 不进行存储,毕竟这个node(对象)里面的内存是动态大小的)
例子这样写的:

		surfHandle.set( newSurfData );
		stat = data.setClean( plug );

我们用的dataHandle有set方法,可以把这个data放回到output attribute node里面(我们之前取到了旧的output node),但是这里比较纳闷的是,我们没办法直接替换它,我们没办法得到这个output attribute node 的引用(MObject引用不安全,所以maya不给我们),对于simple attribute 来说,对应的简单data type,我们可以得到确定的安全的地址,所以这样替换内存中的地址会很安全。
这个set方法会把我们的data放到DataBlock中那个存有output node内存引用所指向的node的内存。
maya通过DataBlock来统一管理node所对应的内存,这样比较安全。那么,plug里面dirty mark怎么消除??它不会自动的clean,我们只有自己来。后面一步是要把plug里面的state归0(因为这个时候plug就是和output node对象兼容吧,嗯,可以考虑plug就是output node的copy node)。
如果是simple data attribute node,那么,我们用dataHandle就可以直接clean这个node 的state了。

——————>
其实,给我的感觉,plug就是output node 的copy object,所以都有state。不是母类,而是wrapper
datablock也是,可以称之为database更形象,而dataHandle就类似selectionList,就是database所对应的wrapper。
我这里database是由一个个wrapper组成的,而每个wrapper所对应的都是maya内部的基础数据结构,每个maya内部数据结构都封装着一个或者多个计算机基础数据结构。
database有查找wrapper的方法,wrapper里有操纵maya内部基础结构的方法。
database是数据从一个地方流向另一个地方所必不可少的,像封装参数对象wrapper(MSelectionList)的MDataBase,以及attribute node wrapper(MDataHandle)的MDataBlock。
对比了一下MSelectionList和MDataHandle,两者非常相似!当然,MPlug也是,这些wrapper方法都有一些共性!!!
所以,当有很多as方法的时候,这些就是wrapper。
当有个类它的返回值都是同一的wrapper,那么,这个就是database类!!

mayaAPI(7)-maya core

maya的核心就是DG node。

maya的API把DG的整体介绍放在shading node 的介绍之后,也许是想让人先实战再学理论吧。。不过这个挺折磨人的。。

这里有个概念,DG。DG(Dependent Graph)是用来描述实体们的关系的一种数学上的手段。

Graph是最上层的概念,在目前的计算机结构里面,是没办法描述无向图的,只能描述有向图,所以说,在计算机世界里面,所谓的图就是指DG。

network也是图的一种,它是加权图,也就是说每个实体之间的edge是带有某种属性的。比如说我们真实的路,那就是有个长度属性。对于描述A城市与B城市之间位置关系,我们可以用距离来描述,当然,描述生活水平关系,我们就可以用GDP属性了(A的生活说平是B的生活水平加上XXX大小的GDP)。在计算机世界里面,我们只有数据结构,只有数据从一个方向往另一方向传递,既然加权是一种表明”加上多少”的概念,那么,我们就可以这样理解传递参数了,传参数就是入栈,就是往一块地方加入数据,这也就是一种加权

所以,在计算机世界里面,实体(称之为类的实例也好,或者简单点的function也好),所组成的基本上都是DG network。

——————>

DAG在maya中的表示方法不知道是不是独创。它是对DG network的一种补充。

因为DG network中,我们不知道这个关系网中有多少个实体,也不知道如何从一个实体找到另一个实体,这些实体本身就只有数据流动的关系,他们并不像树形结构那样,有层次结构。

我们生活中有很多方法都是有等级关系的,并不只是简单的加权关系(加权关系可以形容买卖,路程,能源,许多人与人之间的关系,它是一种平级关系,所做都只是一种复杂化的加减),所以我们为了形容这种等级关系,就需要DAG图。

DAG图同样描述的是一堆实体的关系,但是这种关系是带有路径(层级关系),高层的实体可以控制底层的实体,在maya中,是用parent和child来描述来形象的语言描述这种关系,或者可以自己描述成皇帝和太监,都一样。

实际代码操作中,不像这么简单了,因为计算机里的结构,他只能顺利的描述DG network,我们要描述DAG就有点麻烦,而且还是三维世界的DAG。。。

maya是这样做的,它把层次关系和实体都放在一个DagPath类里面,我们要得到不同的实体,就是用一个默认的DagPath实例来实例化Function Set类。Function Set的实例用create方法就可以让我们创造实体。(Function Set实例还有个getPath方法,可以得到一个新的DagPath实例,这个实例里面就包含了我们想要的层次关系和实体的一些属性)

——————>

我们有DG node这个概念,是因为每个实体它在maya里面,都是一个node,它是一种称呼,以C++而言,它是经过maya的RTTI库封装过的类,很复杂的类。虽然我们图里面有节点这个概念,但是在计算机,或者在maya里面,node并不是等同于节点,它只是一种数学方法在其软件环境里的表达方式。

API告诉我们,DG node:

Dependency graph nodes are the part of a dependency graph which perform computations

DG node就是执行计算的那个部分罢了(也就是算法!)

还有DAG node,这个是不是计算的部分了,它是DAG图中的实体部分(其实只是包含了path和实体的一些通用信息),真正的计算部分,是在Function Set类里面完成的!

——————>

maya API这里说了数据流动是如何引起compute方法的执行:

One optimization is that the DG does not re-evaluate the graph unless it needs to. For example, imagine a revolved surface where there are three nodes, a curve DAG node used as input to the second node, a node which revolves the curve and generates a surface, which is the output to the third node, a DAG node which puts the surface into the DAG. If the input curve was modified, the surface would not be regenerated immediately, it may not happen until the next screen refresh. To make sure that the surface does eventually get rebuilt, modifying the curve would cause all plugs connected to the curve’s output plug to be marked dirty, hence the input to the revolve node would be marked dirty (the curve’s output plug itself would not be marked dirty since it has just been recomputed). When declaring attributes it’s necessary to indicate what attributes affect each other, so in the revolve node, the output attribute is dependent on the input attribute, then marking the input attribute dirty causes the output attribute to be marked dirty. The output of the revolve node is connected to the surface node, marking the revolve node’s output dirty marks the surface as being dirty. So, when the DAG is walked during a screen refresh, since the surface is marked dirty, everything that it is dependent on which has also been marked dirty needs to be re-evaluated.

A input dirty->A output dirty->connected B input dirty->B output dirty

这个就应该是涉及到state属性。

——————>

还有一段,提到了pnts属性,controlPointss属性,这个都是用来存储point信息的。

However, fine-tuning or “tweaking” a model is a necessary operation for building complex models and scenes. So Maya has designed a mechanism for handling these tweaks. Mesh shapes have an attribute, pnts, which stores local changes made to the mesh vertices. Any upstream connection to the mesh shape node which generates a new set of vertices for the mesh will not disturb the pnts attribute. The values in the pnts attribute are added to the coordinates of the mesh. For NURBS surfaces and other control point based nodes, the controlPoints attribute stores tweak information. Maya also has implemented a tweak node which will store tweak information for a control point based node. The tweak node is placed between the control point based node and an upstream deforming node which operates on the control points. The tweak node integrates the tweak information in with the deformed control points to generate the final set of control points that is then passed to the shape. Refer to the manual pages for the tweak node as well as the mesh and NURBS surface shape nodes for more information on the attributes which handle tweak information.

还涉及到tweak属性,原来tweak的意思就是fine-tuning,微调的意思,我们给modle做变形器的时候,就会有tweak节点来做个中转,我们有多个变形器,他们的变形效果就是在tweak里面做整合的。

第 9 页,共 32 页« 最新...78910112030...最旧 »