企业网站功能怎么设计,万全做网站wl17581,php网站模板 免费,wordpress长文章分页插件不过着好象侵权了#xff0c;因为#xff21;#xff2c;#xff29;#xff21;#xff33;声明不得一任何方式传播该手册的部分或全部_炙墨追零
Maya不为插件提供二进制兼容性。每当发布新版本时#xff0c;旧插件的源代码要重新编译。然而#xff0c;我们的目标是保… 不过着好象侵权了因为声明不得一任何方式传播该手册的部分或全部_炙墨追零
Maya不为插件提供二进制兼容性。每当发布新版本时旧插件的源代码要重新编译。然而我们的目标是保持源代码兼容性在大多情况下不需要修改源代码。也许我们以后会提供支持二进制兼容性的API。 IRIX,Windows,Linux和Mac OS X上的Maya API Maya API是提供从内部访问Maya的C API它由一系列与Maya不同功能有关的库组成。这些库是 OpenMaya包含定义节点和命令的基本类 OpenMayaUI包含用于创建用户界面元素如manipulators, contexts, and locators的类 OpenMayaAnim用于创建动画的类包括deformers和inverse kinematics OpenMayaFX包含用于dynamics的类 OpenMayaRender包含用于执行渲染的类 载入插件 有两种装载和卸载插件的方法。最简单的是用Plug-in Manager另一种是用loadPlugin命令。二者都搜索环境变量MAYA_PLUG_IN_PATH指定的路径。 卸载插件 用unloadPlugin命令带上插件名卸载插件。 注释 插件必须在重编译前被卸载否则Maya会崩溃。 卸载插件前必须移除场景中所有的参考和插件中定义的节点还要清空undo队列因为插件的影响还可能被保留其中。 当你在插件使用中强制卸载它就无法重载。因为现存的节点被转换为Unknown重载插件时这些节点不会恢复。 写一个简单的插件 下面演示如何写一个简单的Hello World插件。
#include maya/MSimple.h DeclareSimpleCommand( helloWorld, Alias, 6.0); MStatus helloWorld::doIt( const MArgList ) { printf(Hello World\n); return MS::kSuccess; }
编译后在命令窗口键入helloWorld按数字键盘上的Enter执行。 重要的插件特性 以上例子展示了很多重要特性。 MSimple.h 用于简单的command plug-in的头文件通过DeclareSimpleCommand宏注册新command但这样你只能创建一个command。 注释 可能经常需要创建执行许多特性的插件例如dependency graph nodes和commands在这种情况下MSimple.h不再可用你要自己写注册代码告知Maya插件的功能。 这个宏的主要缺陷是只能创建不能撤销的命令。 MStatus 指明方法是否成功。API类中大部分方法返回MStatus。为防止命名空间冲突我们用MS名字空间如MS::kSuccess 注释 API很少用status codes但如果错误记录被开启额外的错误细节会被写入日志文件。 DeclareSimpleCommand DeclareSimpleCommand宏需要三个参数类名作者名命令版本号 注释 不支持undo的命令不应该改变场景状态否则Maya的undo功能会失效。 写一个与Maya交互的插件 只需要在helloWorld例子上做很少的修改。 以下程序输出Hello后跟输入字符。
#include maya/MSimple.h DeclareSimpleCommand( hello, Alias, 6.0); MStatus hello::doIt( const MArgList args ) { printf(Hello %s\n, args.asString( 0 ).asChar() ); return MS::kSuccess; }
MArgList MArgList类提供和C或C程序中argc/argv相似的作用。该类提供获得多种类型参数如integer,double,string,vector的方法。 下面的例子用于创建螺旋线有pitch和radius两个参数。
#include math.h
#include maya/MSimple.h #include maya/MFnNurbsCurve.h #include maya/MPointArray.h #include maya/MDoubleArray.h #include maya/MPoint.h
DeclareSimpleCommand( helix, Alias - Example, 3.0);
MStatus helix::doIt( const MArgList args ) { MStatus stat;
const unsigned deg 3; // Curve Degree const unsigned ncvs 20; // Number of CVs const unsigned spans ncvs - deg; // Number of spans const unsigned nknots spans2*deg-1;// Number of knots double radius 4.0; // Helix radius double pitch 0.5; // Helix pitch unsigned i;
// Parse the arguments. for ( i 0; i args.length(); i ) if ( MString( -p ) args.asString( i, stat ) MS::kSuccess stat) { double tmp args.asDouble( i, stat ); if ( MS::kSuccess stat ) pitch tmp; } else if ( MString( -r ) args.asString( i, stat ) MS::kSuccess stat) { double tmp args.asDouble( i, stat ); if ( MS::kSuccess stat ) radius tmp; }
MPointArray controlVertices; MDoubleArray knotSequences;
// Set up cvs and knots for the helix // for (i 0; i ncvs; i) controlVertices.append( MPoint( radius * cos( (double)i ), pitch * (double)i, radius * sin( (double)i ) ) );
for (i 0; i nknots; i) knotSequences.append( (double)i );
// Now create the curve // MFnNurbsCurve curveFn;
MObject curve curveFn.create( controlVertices, knotSequences, deg, MFnNurbsCurve::kOpen, false, false, MObject::kNullObj, stat );
if ( MS::kSuccess ! stat ) printf(Error creating curve.\n);
return stat; }
提示 argc/argv和MArgList重要的不同是MArgList中的零元素是参数而不像argc/argv中是命令名。 用插件创建曲线 #include math.h #include maya/MSimple.h #include maya/MPoint.h #include maya/MPointArray.h #include maya/MDoubleArray.h #include maya/MFnNurbsCurve.h
DeclareSimpleCommand( doHelix, Alias - Example, 6.0);
MStatus doHelix::doIt( const MArgList ) { MStatus stat;
const unsigned deg 3; // Curve Degree const unsigned ncvs 20; // Number of CVs const unsigned spans ncvs - deg; // Number of spans const unsigned nknots spans2*deg-1; // Number of knots double radius 4.0; // Helix radius double pitch 0.5; // Helix pitch unsigned i;
MPointArray controlVertices; MDoubleArray knotSequences;
// Set up cvs and knots for the helix // for (i 0; i ncvs; i) controlVertices.append( MPoint( radius * cos( (double)i ), pitch * (double)i, radius * sin( (double)i ) ) );
for (i 0; i nknots; i) knotSequences.append( (double)i );
// Now create the curve // MFnNurbsCurve curveFn;
MObject curve curveFn.create( controlVertices, knotSequences, deg, MFnNurbsCurve::kOpen, false, false, MObject::kNullObj, stat );
if ( MS::kSuccess ! stat ) printf(Error creating curve.\n);
return stat; }
返回页首
LEN3D 二星视客
注册时间: 2002-02-10 帖子: 499
视币:643
发表于: 2005-12-31 11:03 发表主题:
--------------------------------------------------------------------------------
同Maya交互 Maya API包含四种C对象用于和Maya交互它们是wrappers, objects, function sets和proxies。 API中的物体所有权 object和function set的结合类似于wrapper但区别在所有权上。API中物体所有权很重要如果没有很好定义你可能会删除一些系统需要或者已经删除的东西。wrappers,objects,function sets解决了这类问题。 MObject 对所有Maya物体curves, surfaces, DAG nodes, dependency graph nodes, lights, shaders, textures等的访问是通过句柄MObject完成的。这个句柄提供了一些简单的方法来辨别物体类型。MObject的析构函数并不真正删除它所参考的Maya物体调用析构函数只会删除句柄本身而维持所有权。 重要提示 绝对不要在插件调用外储存指向MObject的指针。 Wrappers wrappers为像vectors,matrices这样的简单对象存在。它们一般都被完整的实现带有公用的构造和析构函数。API方法可能会返回一个接下来由你负责的wrapper。你可以任意分配和删除它们。前面例子中的MPointArray和MDoubleArray就是wrappers你拥有wrappers的所有权。 Objects和Function Sets objects和function sets总被一同使用。它们相对独立是因为所有权问题objects总被Maya拥有而function sets属于你。 Function Sets function sets是操作对象的C类。在前面的例子中MFnNurbsCurve就是function setMFn前缀说明了这一点。 下面两行创建新曲线。
MFnNurbsCurve curveFn; MObject curve curveFn.create( ... );
MFnNurbsCurve curveFn;创建包含操作曲线方法的function set, create方法用来创建新曲线。 MObject curve curveFn.create( ... );创建随后你可以任意使用的Maya对象。
如果你再加上一行
curve curveFn.create( ... );
另一条曲线会被创建名为curve的MObject现在指向新创建的这条曲线但原先创建的曲线仍然存在只不过不被句柄参考。 Proxies Maya通过proxy对象创建新对象类型。proxy对象是你创建的但是被Maya占有。 一个普遍的误解是以为可以通过继承现存的function set来创建新对象类型例如MFnNurbsSurface从MFnDagNode派生。但实际上这样不行因为function set完全被你拥有Maya根本不使用它们Maya只用MObject指向的物体。 无类型 分开objects和function sets带来的一个有趣结果API操作对象时可以不顾类型例如
MFnNurbsCurve curveFn; MObject curve curveFn.create( ... ); MFnNurbsSurface surface( curve );
MFnNurbsSurface只操作surface对象以上的例子是错的但是你可能没有发觉。API中的错误检查代码会处理这种初始化错误。 function sets接受任何类型的MObjects如果它不认识该物体就忽略物体并返回错误值。 命名习惯 Maya API使用以下前缀来区别不同类型的对象。
MFn
表示function set
MIt
迭代器功能和function set类似但用于操作单独的对象。
MPx
表示proxy对象你可以继承它们来创建自己的对象类型。
M classes
大多是wrappers但也有例外。MGlobal是包含很多静态方法的类用于全局操作并不需要MObject。 添加参数 上面的螺旋线插件生成简单的曲线但每次结果都一样。 为曲线的例子添加参数 通过一些改变可以添加如radius和pitch这样的参数注意下面函数参数的声明多了一个args
MStatus doHelix::doIt( const MArgList args )
Add the following lines after the variable declarations:
// Parse the arguments. for ( i 0; i args.length(); i ) if ( MString( -p ) args.asString( i ) ) pitch args.asDouble( i ); else if ( MString( -r ) args.asString( i ) ) radius args.asDouble( i );
for循环遍历所有MArgList中的参数两个if语句把参数转换成MString并与可能的值做比较。 如果匹配后一个参数就被转换成double并且赋给对应的值例如
doHelix -p 0.5 -r 5
会生成一个pitch为0.5单位长radius为5单位长的螺旋线。 错误检查 在上面的例子中还未涉及错误检查问题。 大部分的方法提供一个可选参数是一个指向MStatus的指针用于返回值。 下面的参数解析代码包含了错误检查
// Parse the arguments. for ( i 0; i args.length(); i ) if ( MString( -p ) args.asString( i, stat ) MS::kSuccess stat ) { double tmp args.asDouble( i, stat ); // argument can be retrieved as a double if ( MS::kSuccess stat ) pitch tmp; } else if ( MString( -r ) args.asString( i, stat ) MS::kSuccess stat ) { double tmp args.asDouble( i, stat ); // argument can be retrieved as a double if ( MS::kSuccess stat ) radius tmp; }
注意asString()和asDouble()增加的最后一个stat参数用于检查操作是否成功。 例如args.asString(i, stat)可能会返回MS::kFailure索引值大于参数数量或者当不能转换成double值时args.asDouble(i, stat)会失败。 MStatus类 MStatus类用于确定方法是否成功。 Maya API既返回MStatus也给可选的MStatus参数赋值。MStatus类包含error方法并重载了bool操作符它们都能用来检查错误例如
MStatus stat MGlobal::clearSelectionList; if (!stat) { // Do error handling ... }
如果MStatus实例包含错误你可以做以下事
用statusCode方法获得具体的错误原因。 用errorString方法获得错误细节描述。 用perror打印错误细节描述。 用重载的相等和不相等操作符来和某个MStatusCode做比较。 用clear方法把MStatus实例设置为成功状态。 错误记录 同使用MStatus一样你也可以用错误记录检查错误。 开启和关闭错误记录 1. 在MEL中用openMayaPref命令加上-errlog标识。 2. 在插件中调用MGlobal::startErrorLogging()和MGlobal::stopErrorLogging()方法。 一旦开启错误记录每次插件发生错误都会被Maya记录在文件中还会包含错误在何处引发的描述。 默认文件名是OpenMayaErrorLog在当前目录下。 可以用MGlobal::setErrorLogPathName改变错误记录的目录。 提示 插件可以用MGlobal::doErrorLogEntry()向记录文件中添加自己的记录。
--------------------------------------------------------------------------------
用API选择 概览用API选择 一个命令经常从选择列表获取输入。MGlobal::getActiveSelectionList() 方法的结果包含所有当前被选择的物体。通过MSelectionList和MItSelectionList两个API类可以容易的检查和修改选择列表。 MGlobal::setActiveSelectionList() 可以通过MGlobal::setActiveSelectionList()获得当前全局选择列表的拷贝。 在调用MGlobal::setActiveSelectionList()前你对返回的选择列表的任何改变都不会真正影响到场景。 你可以创建自己的MSelectionList来与其它列表合并列表可以用来创建对象集合。 MSelectionList MSelectionList提供向选择列表中添加、删除和遍历对象的方法。 下面的例子打印所有被选中的DAG节点的名字。 简单的插件例子
#include maya/MSimple.h #include maya/MGlobal.h #include maya/MString.h #include maya/MDagPath.h #include maya/MFnDagNode.h #include maya/MSelectionList.h
MStatus pickExample::doIt( const MArgList ) { MDagPath node; MObject component; MSelectionList list; MFnDagNode nodeFn;
MGlobal::getActiveSelectionList( list ); for ( unsigned int index 0; index list.length(); index ) { list.getDagPath( index, node, component ); nodeFn.setObject( node ); printf(%s is selected\n, nodeFn.name().asChar() ); }
return MS::kSuccess; } DeclareSimpleCommand( pickExample, Alias, 1.0 );
MFnDagNode中的setObject()方法被MFnBase的所有子类继承用于设置该function set将要操作的对象。通常这在构造函数中完成但如果function set已经被创建并且你想改变它要操作的对象就使用setObject()这比每次都重新创建和销毁function set有效率的多。 当你选择CVs并调用该插件时不会得到CVs的名字而是得到父对象如curve,surface,mesh的名字并且打印名字的数量和选择物体的数量不同。 Maya简化像CVs这样的对象组件的选择不是把每个组件都添加到选择列表而是添加父对象并把组件当成一个组。 例如当nurbSphereShape1的很多CVs被选中list.getDagPath() 会返回一个指向nurbSphereShape1 的MDagPath和一个包含了所有被选中的CVs的MObject。这里的MDagPath和MObject可以被传递给MItSurfaceCV迭代器来遍历所有选中的CVs。 只要你继续选择一个对象的组件选择列表中的对象只会有一个。然而如果你选择了一个对象中的某些组件和另一个对象中的某些组件然后再选择第一个对象中的某些组件那么第一个对象会在选择列表中出现两次。这样你可以决定物体被选择的顺序。所有组件也被按它们被选中的顺序排列。 MItSelectionList MltSelectionList是一个包含被选择物体的wrapper类它可以通过过滤让你只看到特定类型的对象而MSelectionList不行。
MGlobal::getActiveSelectionList( list ); for ( MItSelectionList listIter( list ); !listIter.isDone(); listIter.next() ) { listIter.getDagPath( node, component ); nodeFn.setObject( node ); printf(%s is selected\n, nodeFn.name().asChar() ); }
上面MSelectionList的例子可以用这些代码代替结果完全相同。 当把MItSelectionList的构造函数改成
MItSelectionList listIter( list, MFn::kNurbsSurface )
这时只会访问NURBS Surface也会忽略surface CVs。如果你只想访问surface CVs可以这么写
MItSelectionList listIter( list, MFn::kSurfaceCVComponent ) setObject()方法 限制 Mesh vertices, faces或edges并不按被选中的顺序返回。 MFn::Type枚举 到处都会用到MFn::Type枚举来确定物件类型。 function sets类有apiType()方法用来确定MObject参考的对象类型还有一个type()方法用来确定function set本身的类型。 MGlobal::getFunctionSetList()返回字符串数组描述了function sets的类型。 MGlobal::selectByName() MGlobal::selectByName()方法将所有匹配的对象添加到当前选择列表例如
MGlobal::selectByName( *Sphere* );
选择所有名字中包含Sphere的东西。 提示 你也可以不必创建MSelectionList而直接用MGlobal::select()往全局选择列表添加对象。
--------------------------------------------------------------------------------
Command plug-ins 向Maya添加commands概览 Plug-ins API支持多种类型的插件 Command plug-ins提供命令来扩展MEL Tool commands获得鼠标输入的插件 Dependency graph plug-ins添加新的操作如dependency graph nodes Device plug-ins允许新的设备与Maya交互 注册commands 你必须知道在Maya中如何正确注册commandsMFnPlugin类用来做这件事。 MFnPlugin 下面的helloWorld用MFnPlugin注册而不像前面那样用宏。
#include stdio.h #include maya/MString.h #include maya/MArgList.h #include maya/MFnPlugin.h #include maya/MPxCommand.h
class hello : public MPxCommand { public: MStatus doIt( const MArgList args ); static void* creator(); };
MStatus hello::doIt( const MArgList args ) { printf(Hello %s\n, args.asString( 0 ).asChar() ); return MS::kSuccess; }
void* hello::creator() { return new hello; }
MStatus initializePlugin( MObject obj ) { MFnPlugin plugin( obj, Alias, 1.0, Any ); plugin.registerCommand( hello, hello::creator ); return MS::kSuccess; }
MStatus uninitializePlugin( MObject obj ) { MFnPlugin plugin( obj ); plugin.deregisterCommand( hello );
return MS::kSuccess; }
注释 所有插件都必须有initializePlugin()和uninitializePlugin()否则不会被载入creator是必须的它允许Maya创建类的实例。 initializePlugin() initializePlugin()可以被当作C或C函数定义。你不定义它插件就不会被装载。 它包含了注册commands, tools, devices等的代码只在插件装载时被立即调用一次。 例如commands和tools通过实例化一个操作MObject的MFnPlugin来注册。这个MObject包含Maya私有信息如插件名字和目录它被传递给MFnPlugin的构造函数。Any表示Maya API的版本是默认值。 MFnPlugin::registerCommand()用来注册hello命令。如果初始化不成功插件会被自动卸载。 uninitializePlugin() uninitializePlugin()用来注销initializePlugin()注册的所有东西只在插件被卸载时被调用一次。Maya会自己管理的对象不需要在这里销毁。 Creator methods 插件要注册的东西Maya事先是不知道的所以没办法决定分配它需要的空间这时creator用来帮助Maya创建这些东西的实例。 MPxCommand 从MPxCommand继承的类至少要定义两个函数doIt和creator。 doIt()和redoIt()方法 doIt()方法是纯虚的而且基类中没有定义creator所以两个都要定义。 简单的command可以让doIt做所有事但在复杂的例子中doIt()用来解析参数列表和选择列表等并用解析的结果设置内部数据最后再调用redoIt()来做真正的工作。这样避免了doIt()和redoIt()间的代码重复。 校验 下面的例子用来演示方法被调用的顺序。
#include stdio.h #include maya/MString.h #include maya/MArgList.h #include maya/MFnPlugin.h #include maya/MPxCommand.h
class commandExample : public MPxCommand { public: commandExample(); virtual ~commandExample(); MStatus doIt( const MArgList ); MStatus redoIt(); MStatus undoIt(); bool isUndoable() const; static void* creator(); };
commandExample::commandExample() { printf(In commandExample::commandExample()\n); } commandExample::~commandExample() { printf(In commandExample::~commandExample()\n); } MStatus commandExample::doIt( const MArgList ) { printf(In commandExample::doIt()\n); return MS::kSuccess; } MStatus commandExample::redoIt() { printf(In commandExample::redoIt()\n); return MS::kSuccess; } MStatus commandExample::undoIt() { printf(In commandExample::undoIt()\n); return MS::kSuccess; } bool commandExample::isUndoable() const { printf(In commandExample::isUndoable()\n); return true; } void* commandExample::creator() { printf(In commandExample::creator()\n); return new commandExample(); }
MStatus initializePlugin( MObject obj ) { MFnPlugin plugin( obj, My plug-in, 1.0, Any ); plugin.registerCommand( commandExample, commandExample::creator ); printf(In initializePlugin()\n); return MS::kSuccess; }
MStatus uninitializePlugin( MObject obj ) { MFnPlugin plugin( obj ); plugin.deregisterCommand( commandExample ); printf(In uninitializePlugin()\n); return MS::kSuccess; }
第一次载入插件时In initializePlugin()被立即打印在命令窗口键入commandExample会得到
In commandExample::creator() In commandExample::commandExample() In commandExample::doIt() In commandExample::isUndoable()
注意析构函数并没有被调用因为command还有可能被undo和redo。 这是Maya的undo机制的工作方式。Command objects负责保留undo自己需要的信息。析构函数在command掉到undo队列的末尾或插件被卸载时才被调用。 如果你修改一下让isUndoable()返回false将得到如下结果
In commandExample::creator() In commandExample::commandExample() In commandExample::doIt() In commandExample::isUndoable() In commandExample::~commandExample()
在这种情况下析构函数立即被调用因为不能undo了Maya认为这样的command不会改变场景所以使用undo和redo时undoIt()和redoIt()不会被调用。 带undo和redo的螺旋线例子 这个插件会把被选中的曲线变成螺旋线。
#include stdio.h #include math.h
#include maya/MFnPlugin.h #include maya/MFnNurbsCurve.h #include maya/MPointArray.h #include maya/MDoubleArray.h #include maya/MPoint.h #include maya/MSelectionList.h #include maya/MItSelectionList.h #include maya/MItCurveCV.h #include maya/MGlobal.h #include maya/MDagPath.h #include maya/MString.h #include maya/MPxCommand.h #include maya/MArgList.h
class helix2 : public MPxCommand { public: helix2(); virtual ~helix2(); MStatus doIt( const MArgList ); MStatus redoIt(); MStatus undoIt(); bool isUndoable() const; static void* creator();
这边的和前面都差不多。
private: MDagPath fDagPath; MPointArray fCVs; double radius; double pitch; };
这个command储存原始的曲线信息用来undo并储存螺旋线的描述用来redo。 非常值得注意的是它不储存MObject而是用一个MDagPath来指向要操作的曲线。因为命令下一次被执行时无法保证MObject继续有效Maya可能会核心转储。然而MDagPath却能保证任何时候都指向正确的曲线。
void* helix2::creator() { return new helix2; }
creator简单返回对象实例。
helix2::helix2() : radius( 4.0 ), pitch( 0.5 ) {}
初始化radius和pitch的值。
helix2::~helix2() {}
这时析构函数不需要做任何事。 注释 不应该删除Maya占有的数据。
MStatus helix2::doIt( const MArgList args ) { MStatus status;
// Parse the arguments. for ( int i 0; i args.length(); i ) if ( MString( -p ) args.asString( i, status ) MS::kSuccess status ) { double tmp args.asDouble( i, status ); if ( MS::kSuccess status ) pitch tmp; } else if ( MString( -r ) args.asString( i, status ) MS::kSuccess status ) { double tmp args.asDouble( i, status ); if ( MS::kSuccess status ) radius tmp; } else { MString msg Invalid flag: ; msg args.asString( i ); displayError( msg ); return MS::kFailure; }
在doIt()里面解析参数并且设置radius和pitch它们将被用在redoIt()中。 从MPxCommand继承的displayError()方法用来在命令窗口输出消息消息都会带上Error:前缀如果用displayWarning()就会带上Warning:前缀。
// Get the first selected curve from the selection list. MSelectionList slist; MGlobal::getActiveSelectionList( slist ); MItSelectionList list( slist, MFn::kNurbsCurve, status ); if (MS::kSuccess ! status) { displayError( Could not create selection list iterator ); return status; }
if (list.isDone()) { displayError( No curve selected ); return MS::kFailure; }
MObject component; list.getDagPath( fDagPath, component );
这段代码从选择列表中取出第一条曲线。
return redoIt(); }
最后调用redoIt()。
MStatus helix2::redoIt() { unsigned i, numCVs; MStatus status; MFnNurbsCurve curveFn( fDagPath );
numCVs curveFn.numCVs(); status curveFn.getCVs( fCVs ); if ( MS::kSuccess ! status ) { displayError( Could not get curves CVs ); return MS::kFailure; }
从被选中的曲线获取CVs并储存在command内的MPointArray中用来在调用undoIt()时把曲线恢复为原状。
MPointArray points(fCVs); for (i 0; i numCVs; i) points[i] MPoint( radius * cos( (double)i ), pitch * (double)i, radius * sin( (double)i ) ); status curveFn.setCVs( points ); if ( MS::kSuccess ! status ) { displayError( Could not set new CV information ); fCVs.clear(); return status; }
这段代码把曲线变为螺旋线。 updateCurve()用来通知Maya几何已被更改需要重绘。
return MS::kSuccess; }
MStatus helix2::undoIt() { MStatus status;
MFnNurbsCurve curveFn( fDagPath ); status curveFn.setCVs( fCVs ); if ( MS::kSuccess ! status) { displayError( Could not set old CV information ); return status; }
这里将曲线的CVs恢复了。 注释 不需要担心CVs数量改变或已被删除你可以假设command执行后的一切改变都被撤销了模型维持不变。
status curveFn.updateCurve(); if ( MS::kSuccess ! status ) { displayError( Could not update curve ); return status; }
fCVs.clear(); return MS::kSuccess; }
只是为了以防万一才调用clear。
bool helix2::isUndoable() const { return true; }
指明该command是可撤销的。
MStatus initializePlugin( MObject obj ) { MFnPlugin plugin( obj, Alias, 1.0, Any); plugin.registerCommand( helix2, helix2::creator );
return MS::kSuccess; }
MStatus uninitializePlugin( MObject obj ) { MFnPlugin plugin( obj ); plugin.deregisterCommand( helix2 );
return MS::kSuccess; }
这里和前面差不多。 Returning results to MEL commands也可以向MEL返回结果这通过叫setResult和appendToResult的一系列从MPxCommand继承的重载函数完成。例如可以这样返回值为4的整数
int result 4; clearResult(); setResult( result );
也可以多次调用appendToResult来返回数组如
MPoint result (1.0, 2.0, 3.0); ... clearResult(); appendToResult( result.x ); appendToResult( result.y ); appendToResult( result.z );
或者直接返回数组
MDoubleArray result; MPoint point (1.0, 2.0, 3.0);
result.append( point.x ); result.append( point.y ); result.append( point.z );
clearResult(); setResult( result ); Syntax objects 当你写语法对象时需要用到MSyntax和MArgDatabase这些类是定义和处理命令标记输入时必需的。 MSyntax用来指定传递给commands的标记和参数。 MArgDatabase用于解析传递给commands的所有标记、参数和对象的类。它接受MSyntax对象来把命令参数解析成容易访问的形式。 注释 MArgParser和MArgDatabase类似但是用于context commands的而不是commands。 Flags Syntax objects需要flags你既要定义短flags又要定义长flags短flags小于等于三个字母长flags大于等于4个字母。 用#define来声明这些flags例如scanDagSyntax使用以下flags
#define kBreadthFlag -b #define kBreadthFlagLong -breadthFirst #define kDepthFlag -d #define kDepthFlagLong -depthFirst
创建Syntax Object 在你的command类中需要写newSyntax方法建立你的语法。它应该是返回MSyntax的静态函数。 在该方法中你应该为syntax object添加必要的flags并返回它。 scanDagSyntax类的newSyntax定义如下
class scanDagSyntax: public MPxCommand { public: ... static MSyntax newSyntax(); ... };
MSyntax scanDagSyntax::newSyntax() { MSyntax syntax;
syntax.addFlag(kBreadthFlag, kBreadthFlagLong); syntax.addFlag(kDepthFlag, kDepthFlagLong); ... return syntax; }
解析参数 按习惯一般在parseArgs方法中解析参数并由doIt调用该方法创建一个局部的用syntax object和命令参数初始化的MArgDatabase。MArgDatabase提供了决定哪些flags被设置的方便方法。 注释 除特别情况外所有API都用Maya内部的厘米和弧度作为单位。
MStatus scanDagSyntax::parseArgs(const MArgList args, MItDag::TraversalType traversalType, MFn::Type filter, bool quiet) { MArgDatabase argData(syntax(), args);
if (argData.isFlagSet(kBreadthFlag)) traversalType MItDag::kBreadthFirst; else if (argData.isFlagSet(kDepthFlag)) traversalType MItDag::kDepthFirst; ... return MS::kSuccess; }
注册 创建syntax object的方法同command一起在initializePlugin中被注册。
MStatus initializePlugin( MObject obj ) { MStatus status; MFnPlugin plugin(obj, Alias - Example, 2.0, Any); status plugin.registerCommand(scanDagSyntax, scanDagSyntax::creator, scanDagSyntax::newSyntax); return status; } Contexts Maya中的contexts用于决定鼠标动作如何被解释。context可以执行commands改变当前选择集或执行绘图操作等。context还能绘制不同的鼠标指针。在Maya中context被表述为tool。 MPxContext MPxContext允许你创建自己的context。 在Maya中context通过一个特殊的command来创建。在这点上context类似shape它通过command创建和修改并具有决定其行为和外观的状态。当你通过继承MPxContext来写一个context时你也应该为它定义一个从MPxContextCommand派生的command。 以下是选取框的例子它会绘制一个OpenGL选取框并选择物体。
const char helpString[] Click with left button or drag with middle button to select;
class marqueeContext : public MPxContext { public: marqueeContext(); virtual void toolOnSetup( MEvent event ); virtual MStatus doPress( MEvent event ); virtual MStatus doDrag( MEvent event ); virtual MStatus doRelease( MEvent event ); virtual MStatus doEnterRegion( MEvent event );
如果虚方法没有被重载MPxContext将执行默认的动作所以你只要写必要的方法即可。
private: short start_x, start_y; short last_x, last_y; MGlobal::ListAdjustment listAdjustment M3dView view; };
marqueeContext::marqueeContext() { setTitleString ( Marquee Tool ); }
构造函数设置了标题文字当工具被选中时会显示在界面上。
void marqueeContext::toolOnSetup ( MEvent ) { setHelpString( helpString ); }
当工具被选中该方法会被调用在提示行显示帮助信息。
MStatus marqueeContext::doPress( MEvent event ) {
当工具被选中并且你按下鼠标键时该方法会被调用。MEvent对象包含了鼠标动作的相关信息如单击的坐标。
if (event.isModifierShift() || event.isModifierControl() ) { if ( event.isModifierShift() ) { if ( event.isModifierControl() ) { // both shift and control pressed, merge new selections listAdjustment MGlobal::kAddToList; } else { // shift only, xor new selections with previous ones listAdjustment MGlobal::kXORWithList; } } else if ( event.isModifierControl() ) { // control only, remove new selections from the previous list listAdjustment MGlobal::kRemoveFromList; } } else listAdjustment MGlobal::kReplaceList;
这里根据键盘动作来决定选择类型。
event.getPosition( start_x, start_y );
获得开始选择处的屏幕坐标。
view M3dView::active3dView(); view.beginGL();
决定激活视图并开启OpenGL渲染。
view.beginOverlayDrawing(); return MS::kSuccess; }
MStatus marqueeContext::doDrag( MEvent event ) {
当你拖动鼠标时该方法会被调用。
event.getPosition( last_x, last_y ); view.clearOverlayPlane();
每次绘制新选择框前该方法都会被调用来清空overlay planes。
glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluOrtho2D( 0.0, (GLdouble) view.portWidth(), 0.0, (GLdouble) view.portHeight() ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glTranslatef(0.375, 0.375, 0.0);
执行视图变换。
glLineStipple( 1, 0x5555 ); glLineWidth( 1.0 ); glEnable( GL_LINE_STIPPLE ); glIndexi( 2 );
选定直线类型。
// Draw marquee // glBegin( GL_LINE_LOOP ); glVertex2i( start_x, start_y ); glVertex2i( last_x, start_y ); glVertex2i( last_x, last_y ); glVertex2i( start_x, last_y ); glEnd();
绘制选择框。
#ifndef _WIN32 glXSwapBuffers(view.display(), view.window() ); #else SwapBuffers(view.deviceContext() ); #endif
交换双缓冲区。
glDisable( GL_LINE_STIPPLE );
恢复绘图模式。
return MS::kSuccess; }
MStatus marqueeContext::doRelease( MEvent event ) {
鼠标键释放时该方法会被调用。
MSelectionList incomingList, marqueeList; MGlobal::ListAdjustment listAdjustment;
view.clearOverlayPlane(); view.endOverlayDrawing(); view.endGL();
绘图完成清空overlay planes关闭OpenGL渲染。
event.getPosition( last_x, last_y );
决定鼠标释放处的坐标。
MGlobal::getActiveSelectionList(incomingList);
获取并保存当前选择列表。
if ( abs(start_x - last_x) 2 abs(start_y - last_y) 2 ) MGlobal::selectFromScreen( start_x, start_y, MGlobal::kReplaceList );
如果开始和结束选择的坐标相同则用单击选取代替包围盒选取。
else // Select all the objects or components within the marquee. MGlobal::selectFromScreen( start_x, start_y, last_x, last_y, MGlobal::kReplaceList );
包围盒选取。
// Get the list of selected items MGlobal::getActiveSelectionList(marqueeList);
获取刚才选定的列表。
MGlobal::setActiveSelectionList(incomingList, \ MGlobal::kReplaceList);
恢复原选择列表。
MGlobal::selectCommand(marqueeList, listAdjustment);
按指定方式修改选择列表。
return MS::kSuccess; }
MStatus marqueeContext::doEnterRegion( MEvent ) { return setHelpString( helpString ); }
当你把鼠标移到任何视图时都会调用该方法。
class marqueeContextCmd : public MPxContextCommand { public: marqueeContextCmd(); virtual MPxContext* makeObj(); static void* creator(); }; MPxContextCommand MPxContextCommand类用来定义创建contexts的特殊command。context commands和一般的commands一样可以用命令行调用或者写入MEL脚本。它们拥有修改context属性的编辑和访问选项。它们创建一个context实例并传给Maya。context commands不能撤销。 创建context command 下面是用来创建前面的选择框context的context command。
marqueeContextCmd::marqueeContextCmd() {}
MPxContext* marqueeContextCmd::makeObj() { return new marqueeContext(); }
用来为Maya创建context实例的方法。
void* marqueeContextCmd::creator() { return new marqueeContextCmd; }
MStatus initializePlugin( MObject obj ) { MStatus status; MFnPlugin plugin( obj, Alias, 1.0, Any); status plugin.registerContextCommand( \ marqueeToolContext, marqueeContextCmd::creator );
用MFnPlugin::registerContextCommand()而不是MFnPlugin::registerCommand()注册context command。
return status; }
MStatus uninitializePlugin( MObject obj ) { MStatus status; MFnPlugin plugin( obj ); status plugin.deregisterContextCommand( \ marqueeToolContext );
用MFnPlugin::deregisterContextCommand()注销context command。
return status; }
创建context就是这么简单。 把context command添加到Maya工具架 有两种方法激活context为当前context。第一种是用setToolTo命令后跟context的名字。 第二种方法就是把context的图表添加到Maya工具架上。Maya工具架可以储存两种按钮command按钮和tool按钮。 下面是用来创建context按钮和tool按钮的MEL命令。
marqueeToolContext marqueeToolContext1; setParent Shelf1; toolButton -cl toolCluster -t marqueeToolContext1 -i1 marqueeTool.xpm marqueeTool1;
这些MEL代码创建一个marqueeToolContext实例并添加到Common工具架。 marqueeTool.xpm是工具的图标名必须在XBMLANGPATH目录下否则就不会显示但工具依然可用。 这些代码既可以手工键入又可以在initializePlugin()中用MGlobal::sourceFile()调用。 Tool property sheets Tool property sheets是用来显示和编辑context属性的交互式编辑器和用来编辑dependency graph node属性的attribute editors类似。 实现一个tool properly sheet必须写两个MEL文件一个用来编辑context一个用来访问context。 一个文件叫yourContextNameProperties.mel另一个叫yourContextNameValues.melyourContextName是context的名字可以用getClassName()方法获得。 Properties.mel用来定义编辑器外观。 Values.mel用来从编辑器获得数值。 要有效实现tool property sheet你必须在context command中实现足够的编辑和访问选项并在MPxContext实现充分的访问方法来设置和获得内部参数。 MPxToolCommand MPxToolCommand是创建用来在context中执行的commands的基类。tool commands和一般的commands一样用command flags定义可以用命令行调用。但它们要做额外的工作因为它们不从Maya调用而是从MPxContext调用。这些工作用来让Maya正确执行undo/redo和日志策略。 如果context想执行自己的command必须在注册context和context command时注册这个command。一个context只能和一个tool command对应。 下面是综合了螺旋线和选取框的tool command的例子。
#define NUMBER_OF_CVS 20
class helixTool : public MPxToolCommand { public: helixTool(); virtual ~helixTool(); static void* creator();
MStatus doIt( const MArgList args ); MStatus redoIt(); MStatus undoIt(); bool isUndoable() const; MStatus finalize(); static MSyntax newSyntax();
这些方法都差不多但是加了finalize()用来提供command用法的帮助。
void setRadius( double newRadius ); void setPitch( double newPitch ); void setNumCVs( unsigned newNumCVs ); void setUpsideDown( bool newUpsideDown );
当tool command属性由context设置时这些方法是必要的。
private: double radius; // Helix radius double pitch; // Helix pitch unsigned numCV; // Helix number of CVs bool upDown; // Helis upsideDown MDagPath path; // Dag path to the curve. // Dont save the pointer! };
void* helixTool::creator() { return new helixTool; }
helixTool::~helixTool() {}
这里和前面的螺旋线例子一样。
helixTool::helixTool() { numCV NUMBER_OF_CVS; upDown false; setCommandString( helixToolCmd ); }
构造函数保存MEL command的名字以待在finalize()中使用。
MSyntax helixTool::newSyntax() { MSyntax syntax;
syntax.addFlag(kPitchFlag, kPitchFlagLong, MSyntax::kDouble); syntax.addFlag(kRadiusFlag, kRadiusFlagLong, MSyntax::kDouble); syntax.addFlag(kNumberCVsFlag, kNumberCVsFlagLong, MSyntax::kUnsigned); syntax.addFlag(kUpsideDownFlag, kUpsideDownFlagLong, MSyntax::kBoolean);
return syntax; }
MStatus helixTool::doIt( const MArgList args ) { MStatus status;
status parseArgs(args);
if (MS::kSuccess ! status) return status;
return redoIt(); }
MStatus helixTool::parseArgs(const MArgList args) { MStatus status; MArgDatabase argData(syntax(), args);
if (argData.isFlagSet(kPitchFlag)) { double tmp; status argData.getFlagArgument(kPitchFlag, 0, tmp); if (!status) { status.perror(pitch flag parsing failed.); return status; } pitch tmp; }
if (argData.isFlagSet(kRadiusFlag)) { double tmp; status argData.getFlagArgument(kRadiusFlag, 0, tmp); if (!status) { status.perror(radius flag parsing failed.); return status; } radius tmp; }
if (argData.isFlagSet(kNumberCVsFlag)) { unsigned tmp; status argData.getFlagArgument(kNumberCVsFlag, 0, tmp); if (!status) { status.perror(numCVs flag parsing failed.); return status; } numCV tmp; }
if (argData.isFlagSet(kUpsideDownFlag)) { bool tmp; status argData.getFlagArgument(kUpsideDownFlag, 0, tmp); if (!status) { status.perror(upside down flag parsing failed.); return status; } upDown tmp; }
return MS::kSuccess; }
MStatus helixTool::redoIt() { MStatus stat;
const unsigned deg 3; // Curve Degree const unsigned ncvs NUMBER_OF_CVS;// Number of CVs const unsigned spans ncvs - deg; // Number of spans const unsigned nknots spans2*deg-1;// Number of knots unsigned i; MPointArray controlVertices; MDoubleArray knotSequences;
int upFactor; if (upDown) upFactor -1; else upFactor 1;
// Set up cvs and knots for the helix // for (i 0; i ncvs; i) controlVertices.append( MPoint( radius * cos( (double)i ), upFactor * pitch * (double)i, radius * sin( (double)i ) ) );
for (i 0; i nknots; i) knotSequences.append( (double)i );
// Now create the curve // MFnNurbsCurve curveFn;
MObject curve curveFn.create( controlVertices, knotSequences, deg, MFnNurbsCurve::kOpen, false, false, MObject::kNullObj, stat );
if ( !stat ) { stat.perror(Error creating curve); return stat; }
stat curveFn.getPath( path );
return stat; }
MStatus helixTool::undoIt() { MStatus stat; MObject transform path.transform(); stat MGlobal::removeFromModel( transform ); return stat; }
bool helixTool::isUndoable() const { return true; }
这里和前面的例子类似其实只要做很少的修改就可以把command变成tool。
MStatus helixTool::finalize() { MArgList command; command.addArg( commandString() ); command.addArg( MString(kRadiusFlag) ); command.addArg( radius ); command.addArg( MString(kPitchFlag) ); command.addArg( pitch ); command.addArg( MString(kNumberCVsFlag) ); command.addArg( (int)numCV ); command.addArg( MString(kUpsideDownFlag) ); command.addArg( upDown ); return MPxToolCommand::doFinalize( command ); }
因为tool从UI调用而不是命令行所以不能像一般command那样输出日志所以需要finalize()来做这件事。
void helixTool::setRadius( double newRadius ) { radius newRadius; }
void helixTool::setPitch( double newPitch ) { pitch newPitch; }
void helixTool::setNumCVs( unsigned newNumCVs ) { numCV newNumCVs; }
void helixTool::setUpsideDown( double newUpsideDown ) { upDown newUpsideDown; }
const char helpString[] Click and drag to draw helix;
class helixContext : public MPxContext {
用来执行helixTool command的context。
public: helixContext(); virtual void toolOnSetup( MEvent event ); virtual MStatus doPress( MEvent event ); virtual MStatus doDrag( MEvent event ); virtual MStatus doRelease( MEvent event ); virtual MStatus doEnterRegion( MEvent event );
private: short startPos_x, startPos_y; short endPos_x, endPos_y; unsigned numCV; bool upDown; M3dView view; GLdouble height,radius; };
helixContext::helixContext() { setTitleString( Helix Tool ); }
void helixContext::toolOnSetup( MEvent ) { setHelpString( helpString ); }
MStatus helixContext::doPress( MEvent event ) { event.getPosition( startPos_x, startPos_y ); view MGlobal::active3dView(); view.beginGL(); view.beginOverlayDrawing(); return MS::kSuccess; }
和前面的例子差不多只是在doPress()方法中不需要检测键盘。
MStatus helixContext::doDrag( MEvent event ) { event.getPosition( endPos_x, endPos_y ); view.clearOverlayPlane(); glIndexi( 2 );
int upFactor; if (upDown) upFactor 1; else upFactor -1;
// Draw the guide cylinder // glMatrixMode( GL_MODELVIEW ); glPushMatrix(); glRotatef( upFactor * 90.0, 1.0f, 0.0f, 0.0f ); GLUquadricObj *qobj gluNewQuadric(); gluQuadricDrawStyle(qobj, GLU_LINE); GLdouble factor (GLdouble)numCV; radius fabs(endPos_x - startPos_x)/factor 0.1; height fabs(endPos_y - startPos_y)/factor 0.1; gluCylinder( qobj, radius, radius, height, 8, 1 ); glPopMatrix();
绘制表示螺旋线轮廓的圆柱。
#ifndef _WIN32 glXSwapBuffers(view.display(), view.window() ); #else SwapBuffers(view.deviceContext() ); #endif
return MS::kSuccess; }
MStatus helixContext::doRelease( MEvent ) { // Clear the overlay plane restore from overlay drawing // view.clearOverlayPlane(); view.endOverlayDrawing(); view.endGL();
helixTool * cmd (helixTool*)newToolCommand(); cmd-setPitch( height/NumCVs ); cmd-setRadius( radius ); cmd-setNumCVs( numCV ); cmd-setUpsideDown( upDown ); cmd-redoIt(); cmd-finalize();
调用helixTool::creator创建真正的command调用redoIt() 生成数据调用finalize()输出日志。
return MS::kSuccess; }
MStatus helixContext::doEnterRegion( MEvent ) { return setHelpString( helpString ); }
void helixContext::getClassName( MString name ) const { name.set(helix); }
下面的四个方法用于context和context command的编辑和访问方法间的交互会被tool property sheet调用。MToolsInfo::setDirtyFlag()告诉tool property sheet参数改变需要重绘。
void helixContext::setNumCVs( unsigned newNumCVs ) { numCV newNumCVs; MToolsInfo::setDirtyFlag(*this); }
void helixContext::setUpsideDown( bool newUpsideDown ) { upDown newUpsideDown; MToolsInfo::setDirtyFlag(*this); }
unsigned helixContext::numCVs() { return numCV; }
bool helixContext::upsideDown() { return upDown; }
下面的类和选取框例子相仿。
class helixContextCmd : public MPxContextCommand { public: helixContextCmd(); virtual MStatus doEditFlags(); virtual MStatus doQueryFlags(); virtual MPxContext* makeObj(); virtual MStatus appendSyntax(); static void* creator();
protected: helixContext * fHelixContext; };
helixContextCmd::helixContextCmd() {}
MPxContext* helixContextCmd::makeObj() { fHelixContext new helixContext(); return fHelixContext; }
void* helixContextCmd::creator() { return new helixContextCmd; }
下面的方法做参数解析。有两类参数一类修改context属性一类访问context属性。 注释 参数解析通过MPxContextCommand::parser()方法完成它返回的MArgParser类与MArgDatabase类相似。
MStatus helixContextCmd::doEditFlags() { MArgParser argData parser();
if (argData.isFlagSet(kNumberCVsFlag)) { unsigned numCVs; status argData.getFlagArgument(kNumberCVsFlag, 0, numCVs); if (!status) { status.perror(numCVs flag parsing failed.); return status; } fHelixContext-setNumCVs(numCVs); }
if (argData.isFlagSet(kUpsideDownFlag)) { bool upsideDown; status argData.getFlagArgument(kUpsideDownFlag, 0, upsideDown); if (!status) { status.perror(upsideDown flag parsing failed.); return status; } fHelixContext-setUpsideDown(upsideDown); }
return MS::kSuccess; }
MStatus helixContextCmd::doQueryFlags() { MArgParser argData parser();
if (argData.isFlagSet(kNumberCVsFlag)) { setResult((int) fHelixContext-numCVs()); } if (argData.isFlagSet(kUpsideDownFlag)) { setResult(fHelixContext-upsideDown()); }
return MS::kSuccess; }
MStatus helixContextCmd::appendSyntax() { MStatus status;
MSyntax mySyntax syntax();
if (MS::kSuccess ! mySyntax.addFlag(kNumberCVsFlag, kNumberCVsFlagLong, MSyntax::kUnsigned)) { return MS::kFailure; }
if (MS::kSuccess ! mySyntax.addFlag(kUpsideDownFlag, kUpsideDownFlagLong, MSyntax::kBoolean)) { return MS::kFailure; }
return MS::kSuccess; }
MStatus initializePlugin( MObject obj ) { MStatus status;
MFnPlugin plugin( obj, Alias, 1.0, Any);
// Register the context creation command and the tool // command that the helixContext will use. // status plugin.registerContextCommand( helixToolContext, helixContextCmd::creator, helixToolCmd, helixTool::creator, helixTool::newSyntax); if (!status) { status.perror(registerContextCommand); return status; }
return status; }
initializePlugin()同时注册command和对应的context。
MStatus uninitializePlugin( MObject obj) { MStatus status; MFnPlugin plugin( obj );
// Deregister the tool command and the context // creation command. // status plugin.deregisterContextCommand( helixToolContext helixToolCmd); if (!status) { status.perror(deregisterContextCommand); return status; }
return status; }
必须使用和选取框例子中类似的MEL代码把helixTool添加到用户界面。
--------------------------------------------------------------------------------
DAG层级 DAG层级概览 在Maya中有向无环图DAG用来定义如坐标、方向、几何体尺寸等元素。DAG包含两种DAG节点transforms和shapes。 Transform nodes保存变换信息平移、旋转、缩放等和父子关系信息。例如你有一个手的模型你只想通过一次变换操作同时完成对手掌和手指的旋转而不是单独操作它们这时手掌和手指就共享一个父级transform节点。 Shape nodes参考几何体不提供父子关系信息和变换信息。 在最简单的情形下DAG描述了一个几何体如何构成对象实例。例如当你创建一个球体你既创造了表示球体本身的shape node又创造了允许你指定球体位置、大小和旋转的transform nodeshape node是transform node的子节点。 节点Nodes transform nodes可拥有多个子节点这些子节点被组合起来放在transform node之下。对节点的组合允许节点间共享变换信息并被当作一个单元处理。 实例化 transform node和shape nodes都可以拥有多个父节点这些节点是已被实例化的。实例化可以减少模型的几何体储存量。例如当你建一棵树的模型时你可以创建许多独立的叶子但这会带来很大的数据量因为每片叶子都会有自己的transform nodes和shape nodes及NURBS或多边形数据。但是你也可以只创建一片叶子并将它实例化许多次来创建一系列相同的叶子并把它们各自正确摆放在树枝上这样树叶的shape node和NURBS或多边形数据就被共享了。
--------------------------------------------------------------------------------
PlugInsDAGhierarchya.jpg 描述:
文件大小: 5.89 KB 看过的: 文件被下载或查看 229 次
--------------------------------------------------------------------------------
返回页首
LEN3D 二星视客
注册时间: 2002-02-10 帖子: 499
视币:643
发表于: 2005-12-31 11:08 发表主题:
--------------------------------------------------------------------------------
图示的DAG层级有三个transform nodesTransform1, Transform2, Transform3和一个shape nodeLeaf当Transform3和Leaf被实例化后会显示两片叶子。 Transforms和Shapes DAG节点是DAG中的简单实体。它可能拥有双亲、兄弟和孩子并掌握它们的信息但它不必知道变换和几何信息。Transforms和Shapes是从DAG节点派生的两种节点。transform nodes只负责变换平移、旋转和缩放而不包含几何信息shape nodes只负责几何信息而不包含变换。这意味着一个几何体需要两个节点一个shape node直接位于其父级一个transform node位于shape node的父级。 例如 MFnDagNode用来决定节点有哪些父节点。 MFnTransformNode继承自MFnDagNode的用来操作transform nodes的function set可以获取并设置变换。 MFnNurbsSurface是多种操作不同类型的shape nodes的function set中的一种也从MFnDagNode派生有获取和设置曲面CVs等的方法。 DAG路径paths 路径通过一系列节点指定某个节点或节点实例在DAG中的唯一位置这一系列节点从根节点开始直到目标节点。到同一个目标节点的不同路径对应于一个目标节点的实例。路径的显示形式是从根节点开始的一系列节点的名字用|符号隔开。 DAG路径和API中世界空间的操作 因为DAG路径表示一个shape是如何被插入场景的所以世界空间的任何操作都要通过DAG路径完成。如果试图用MObject句柄获得某个节点组件的世界空间坐标只会导致失败因为没有DAG路径Maya无法决定对象的世界空间坐标只有DAG路径能唯一指定节点的某个实例。几乎所有能返回MObject的方法都会返回MDagPath句柄用于指明路径。所有MFn类都既可以用MObject又可以用MDagPath构造。用MObject进行世界空间操作会失败换作MDagPath则会成功。 添加或删除节点 MDagPath用一个节点的堆栈来表示路径根节点在堆栈底部。push()和pop()方法可以用来添加或删除节点。 注释 这些方法不会改变真正的DAG结构只会改变MDagPath对象本身。 包含和排除矩阵 因为DAG中的节点存在于不同的DAG层级所以每个节点上可能有不同的变换积累。MDagPath允许把这些变换通过inclusiveMatrix()和exclusiveMatrix()两种模式返回。 inclusive matrix考虑了最后一个节点对变换的影响。 exclusive matrix不考虑最后一个节点对变换的影响。 例如如果一条路径定义如下
|RootTransform|Transform1|Transform2|Shape
inclusive matrix考虑RootTransform, Transform1和Transform2的影响而exclusive matrix只考虑RootTransform和Transform1的影响。 为何将shape node添加到DAG路径 在Maya中物体级别的选择实际上是选择了shape node的父级transform node当用MGlobal::getActiveSelectionList()访问选择集时MDagPath只会返回transform node而不是真正的shape。extendToShape()可以方便的把shape node添加到路径末端。 应用于某种MDagPath的function set通过路径的最后一个node获得。如果最后一个node是transform node就可以获得用来操作MDagPath实例的function set。如果最后一个node是shape node就可以获得用来操作shape的function set。 唯一的名字 使用DAG路径后对象名字可以重用只要同名的对象不在相同的父级节点下。
返回页首
--------------------------------------------------------------------------------
Generalized instancing Maya支持generalized instancing意即实例化同一个节点的节点不必互为
--------------------------------------------------------------------------------
返回页首
--------------------------------------------------------------------------------
Node 2和Node 4不是兄弟但它们都实例化Node 3。可以创造更复杂的层级只要不破坏DAG的无环性质即可。 带有多Shapes的Transforms 一个transform node可以有任意数目的子transform nodes。一般上一个transform node只能有一个子shape node当通过交互窗口查看DAG时总是可以发现这一点。然而通过API检查DAG时你会发现transform node有多个子shape node这在原始shape被dependency graph修改后发生。为了保持dependency graph的结果修改后的shape会被放在和原始shape同一个transform node之下。这时只有最终的结果会被显示在交互窗口。
--------------------------------------------------------------------------------
|Transform1|Shape1是原始对象而|Transform1|Shape1a是在交互窗口中真正可见的对象。|Transform1|Shape1也被称为中间对象。这点对后面的dependency graph很重要。 警告 如果你对一个最后一个transform node包含多个子shape node的路径使用MDagPath::extendToShape()第一个shape node会被添加到路径末端如果这不是你想要的建议你用MDagPath::child()和MDagPath::childCount() 方法代替extendToShape()方法。 The Underworld The underworld指的是shape node的参数空间例如NURBS曲面的UV空间。节点和整个子图都可能定义在这样的underworld空间。 例如在NURBS曲面上定义一条曲线的transform node和shape node。曲线上的控制点位于曲面的UV空间。指定underworld中节点的路径位于shape node之下。underworld路径的第一个节点定义在shape的参数空间大多属于transform node。 underworld路径和正常路径的表示类似都使用|符号不同点是在shape node和underworld路径的根节点间用-符号隔开。 例如NURBS曲面上一条曲线的路径完整表示为 |SurfaceTransform|NURBSSurface-UnderworldTransform|CurvesShape。underworlds可以被递归定义只要一个underworld还有参数空间就可以有更底层的underworld。 MDagPath包括了访问underworld不同路径的方法。MDagPath::pathCount()返回MDagPath表示的路径总数。在上面的曲面上的曲线的例子中如果MDagPath表示到curve shape的路径那么pathCount为2。MDagPath::getPath()既返回underworld又返回3D空间的路径。Path 0总指定3D空间Path 1指定Path 0后的underworld路径Path 2指定Path 1后的underworld路径依此
--------------------------------------------------------------------------------
返回页首
遍历DAG实例 下面的scanDagSyntaxCmd例子示范了如何以深度优先或广度优先的方式遍历DAG这对文件转换器之类的要遍历DAG的插件编写很有帮助。
class scanDagSyntax: public MPxCommand { public: scanDagSyntax() {}; virtual ~scanDagSyntax(); static void* creator(); static MSyntax newSyntax(); virtual MStatus doIt( const MArgList );
这是一个简单的例子所以不提供undoIt()和redoIt()。
private: MStatus parseArgs( const MArgList args, MItDag::TraversalType traversalType, MFn::Type filter, bool quiet); MStatus doScan( const MItDag::TraversalType traversalType, MFn::Type filter, bool quiet); void printTransformData(const MDagPath dagPath, bool quiet); };
scanDagSyntax::~scanDagSyntax() {}
void* scanDagSyntax::creator() { return new scanDagSyntax; }
MSyntax scanDagSyntax::newSyntax() { MSyntax syntax;
syntax.addFlag(kBreadthFlag, kBreadthFlagLong); syntax.addFlag(kDepthFlag, kDepthFlagLong); syntax.addFlag(kCameraFlag, kCameraFlagLong); syntax.addFlag(kLightFlag, kLightFlagLong); syntax.addFlag(kNurbsSurfaceFlag, kNurbsSurfaceFlagLong); syntax.addFlag(kQuietFlag, kQuietFlagLong);
return syntax; }
MStatus scanDagSyntax::doIt( const MArgList args ) { MItDag::TraversalType traversalType MItDag::kDepthFirst; MFn::Type filter MFn::kInvalid; MStatus status; bool quiet false;
DAG迭代器可以被设定为只访问特定的类型如果用MFn::kInvalid模式则访问所有DAG节点。
status parseArgs ( args, traversalType, filter, quiet ); if (!status) return status;
return doScan( traversalType, filter, quiet); };
doIt()方法简单的调用一些做真正工作的辅助方法。 MStatus scanDagSyntax::parseArgs( const MArgList args, MItDag::TraversalType traversalType, MFn::Type filter, bool quiet) { MStatus stat; MArgDatabase argData(syntax(), args);
MString arg;
if (argData.isFlagSet(kBreadthFlag)) traversalType MItDag::kBreadthFirst; else if (argData.isFlagSet(kDepthFlag)) traversalType MItDag::kDepthFirst;
if (argData.isFlagSet(kCameraFlag)) filter MFn::kCamera; else if (argData.isFlagSet(kLightFlag)) filter MFn::kLight; else if (argData.isFlagSet(kNurbsSurfaceFlag)) filter MFn::kNurbsSurface;
if (argData.isFlagSet(kQuietFlag)) quiet true;
return stat; }
DAG迭代器能以深度优先或广度优先的方式访问DAG。这个例子只访问cameras, lights和NURBS surfaces但事实上可以访问MFn::Type中的任何类型。
MStatus scanDagSyntax::doScan( const MItDag::TraversalType traversalType, MFn::Type filter, bool quiet) { MStatus status;
MItDag dagIterator( traversalType, filter, status); if ( !status) { status.perror(MItDag constructor); return status; } // Scan the entire DAG and output the name and depth of each node
if (traversalType MItDag::kBreadthFirst) if (!quiet) cout endl Starting Breadth First scan of the Dag; else if (!quiet) cout endl Starting Depth First scan of the Dag;
广度优先遍历意思是先访问兄弟再访问孩子深度优先遍历先访问孩子再访问兄弟。
switch (filter) { case MFn::kCamera: if (!quiet) cout : Filtering for Cameras\n; break; case MFn::kLight: if (!quiet) cout : Filtering for Lights\n; break; case MFn::kNurbsSurface: if (!quiet) cout : Filtering for Nurbs Surfaces\n; break; default: cout endl; }
int objectCount 0; for ( ; !dagIterator.isDone(); dagIterator.next() ) {
MDagPath dagPath;
status dagIterator.getPath(dagPath); if ( !status ) { status.perror(MItDag::getPath); continue; }
MItDag::getPath()获取迭代器的当前对象的参考。DAG路径可提供给function set以操作对象。不推荐用迭代器重新排列DAG。
MFnDagNode dagNode(dagPath, status); if ( !status ) { status.perror(MFnDagNode constructor); continue; }
if (!quiet) cout dagNode.name() : dagNode.typeName() endl;
if (!quiet) cout dagPath: dagPath.fullPathName() endl;
objectCount 1; if (dagPath.hasFn(MFn::kCamera)) {
这里检查当前对象是否为摄像机是则输出摄像机信息。
MFnCamera camera (dagPath, status); if ( !status ) { status.perror(MFnCamera constructor); continue; }
// Get the translation/rotation/scale data printTransformData(dagPath, quiet);
// Extract some interesting Camera data if (!quiet) { cout eyePoint: camera.eyePoint(MSpace::kWorld) endl; cout upDirection: camera.upDirection(MSpace::kWorld) endl; cout viewDirection: camera.viewDirection(MSpace::kWorld) endl; cout aspectRatio: camera.aspectRatio() endl; cout horizontalFilmAperture: camera.horizontalFilmAperture() endl; cout verticalFilmAperture: camera.verticalFilmAperture() endl; } } else if (dagPath.hasFn(MFn::kLight)) {
若对象为灯光则输出灯光信息。
MFnLight light (dagPath, status); if ( !status ) { status.perror(MFnLight constructor); continue; }
// Get the translation/rotation/scale data printTransformData(dagPath, quiet);
// Extract some interesting Light data MColor color;
color light.color(); if (!quiet) { cout color: [ color.r , color.g , color.b ]\n; } color light.shadowColor(); if (!quiet) { cout shadowColor: [ color.r , color.g , color.b ]\n;
cout intensity: light.intensity() endl; } } else if (dagPath.hasFn(MFn::kNurbsSurface)) {
若对象为NURBS曲面则输出其信息。 MFnNurbsSurface surface (dagPath, status); if ( !status ) { status.perror(MFnNurbsSurface constructor); continue; }
// Get the translation/rotation/scale data printTransformData(dagPath, quiet);
// Extract some interesting Surface data if (!quiet) { cout numCVs: surface.numCVsInU() * surface.numCVsInV() endl; cout numKnots: surface.numKnotsInU() * surface.numKnotsInV() endl; cout numSpans: surface.numSpansInU() * surface.numSpansInV() endl; } } else {
为其它类型则只输出变换信息。
// Get the translation/rotation/scale data printTransformData(dagPath, quiet); } }
if (!quiet) { cout.flush(); } setResult(objectCount); return MS::kSuccess; }
void scanDagSyntax::printTransformData(const MDagPath dagPath, bool quiet) {
该方法用于打印DAG节点的变换信息。
MStatus status; MObject transformNode dagPath.transform(status); // This node has no transform - i.e., its the world node if (!status status.statusCode () MStatus::kInvalidParameter) return; MFnDagNode transform (transformNode, status); if (!status) { status.perror(MFnDagNode constructor); return; } MTransformationMatrix matrix (transform.transformationMatrix());
if (!quiet) { cout translation: matrix.translation(MSpace::kWorld) endl; } double threeDoubles[3]; MTransformationMatrix::RotationOrder rOrder;
matrix.getRotation (threeDoubles, rOrder, MSpace::kWorld); if (!quiet) { cout rotation: [ threeDoubles[0] , threeDoubles[1] , threeDoubles[2] ]\n; } matrix.getScale (threeDoubles, MSpace::kWorld); if (!quiet) { cout scale: [ threeDoubles[0] , threeDoubles[1] , threeDoubles[2] ]\n; } }
MStatus initializePlugin( MObject obj ) { MStatus status;
MFnPlugin plugin ( obj, Alias - Example, 2.0, Any ); status plugin.registerCommand( scanDagSyntax, scanDagSyntax::creator, scanDagSyntax::newSyntax );
return status; }
MStatus uninitializePlugin( MObject obj ) { MStatus status;
MFnPlugin plugin( obj ); status plugin.deregisterCommand( scanDagSyntax );
return status; }
Dependency graph插件 Dependency graph插件概览 Dependency graph是Maya的中心它被用在动画和构造记录中。你可以为它添加新的节点来支持全新的操作。 父类描述 可以从12个父类派生新节点这些类是 MPxNode 允许创建新的dependency node从最基本的DG节点派生无继承行为。 MPxLocatorNode 允许创建新的locator node这是DAG对象不能渲染但可以绘制在3D视图中。 MPxIkSolverNode 允许创建新类型的IK解析器。 MPxDeformerNode 允许创建新的变形器。 MPxFieldNode 允许创建新类型的动态场。 MPxEmitterNode 允许创建新类型的动态发射器。 MPxSpringNode 允许创建新类型的动态弹簧。 MPxManipContainer 允许创建新类型的操纵器。 MPxSurfaceShape 允许创建新的DAG对象经常用来创建新类型的shape也可以用在其它方面。 MPxObjectSet 允许创建新类型的集合。 MPxHwShaderNode 允许创建新类型的硬件着色器。 MPxTransform 允许创建新类型的变换矩阵。 基本例子 下面的例子是简单的用户定义的DG节点输入浮点数输出该数的正弦值。
#include string.h #include iostream.h #include math.h
#include maya/MString.h #include maya/MFnPlugin.h
这还是一个插件所以需要MFnPlugin.h但用不同的方法注册节点。
#include maya/MPxNode.h #include maya/MTypeId.h #include maya/MPlug.h #include maya/MDataBlock.h #include maya/MDataHandle.h
大多插件DG节点都要用到这些头文件。
#include maya/MFnNumericAttribute.h
有多种不同的attributes你需要什么依赖于你的节点类型在本例中只用到数字数据。
class sine : public MPxNode {
从MPxNode派生用户定义DG节点。
public: sine();
每当该节点实例被创建时都会调用构造函数可能发生在createNode命令被调用或MFnDependencyNode::create()被调用等时候。
virtual ~sine();
析构函数只在节点真正被删除时被调用。由于Maya的undo队列删除节点并不真正调用析构函数因此如果删除被撤销就可以不重新创建节点而直接恢复它。一般只当undo队列被清空时已删除的节点的析构函数才会被调用。
virtual MStatus compute( const MPlug plug, MDataBlock data );
compute()是节点的关键部分用来真正通过节点的输入产生输出。
static void* creator();
creator()方法的职责和command中的creator一样它允许Maya创建节点实例。每次需要新节点实例时它都可能由createNode或MFnDependencyNode::create()方法。
static MStatus initialize();
initialize()方法由注册机制在插件被载入时立即调用用来定义节点的输入和输出如attributes。
public: static MObject input; static MObject output;
这两个MObject是正弦节点的attributes你可以给节点的attributes起任何名字这里用input和output只是为了清晰。
static MTypeId id;
每个节点都需要一个唯一的标识符用在MFnDependencyNode::create()中以指明创建哪个节点并用于Maya文件格式。 局部测试中你可以使用任何介于0x00000000和0x0007ffff之间的标识符但对于任何想永久使用的节点你应该从Alias技术支持获得唯一的id。
};
MTypeId sine::id( 0x80000 );
初始化节点标识符。
MObject sine::input; MObject sine::output;
初始化attributes为NULL。
void* sine::creator() { return new sine; }
这里creator()方法简单返回新节点实例。在更复杂的情况中可能需要相互连接很多节点可以只定义一个creator来分配和连接所有节点。
MStatus sine::initialize() {
MFnNumericAttribute nAttr;
本例只用到数字数据所以只需要MFnNumericAttribute。
output nAttr.create( output, out, MFnNumericData::kFloat, 0.0 ); nAttr.setWritable(false); nAttr.setStorable(false);
定义输出attribute。定义attribute时你必须指明一个长名字大于等于四个字符和一个短名字小于等于三个字符。这些名字用在MEL脚本和UI编辑器中以区别特殊的attributes。除特殊情况外建议attribute的长名字和其C中的名字相同本例中都取为output。 create方法还指明了attribute的类型这里为浮点数(MFnNumericData::kFloat)并初始化为0a 评论这张