Category Archives: Maya API

Use Partial To Query UI Control

from functools import partial

def foo(a, arg=None):
print ‘first’,a
print ‘2nd’,arg
b= partial(foo, ‘default’)
b()
#first default
#2nd None
b(‘addon’)
#first default
#2nd addon
b
#<functools.partial object at 0x74c60>

Partial is a way to pack a func with a preset argument.  In the example above, calling b() has the same effect of calling foo(‘default’), and calling b(‘addon’) equals foo(‘default’, ‘addon’). This is quite useful when you want to query values from UI controls in Maya. For example:

import maya.cmds as cmds
from functools import partial
def foo(inst=None, arg=None):
retrieved = cmds.floatSliderGrp(inst.aSlider, query=True, value=True)
# omitted do something with retrieved

class Bar(object):
def __init__(self):
# omitted ui setup
self.aSlider = cmds.floatSliderGrp( label=’foo value’, v=0.5, min=0, max=1, cc=partial(foo, self) )
# omitted ui setup

So aSlider is a slider control as a member of Bar, with its change command assigned to function foo(). Need to expose the control to foo(), so the value can be queried for use. Since cc flag must be assigned to a function object, cc = foo(self) cannot work. Partial packed foo(self) into an object and solved this problem.

Leave a comment

Filed under Maya API, Python

PyQt4 Inside Maya2011

Since the version of 2011, Maya User Interface is based on Qt. Using PyQt4 inside Maya2011 should be easier. Before version 2011, PyQt4 is hooked to Maya by pumpThread, and it is not a part of Maya UI, and will always annoyingly drop to background as soon as the Maya UI is focused.  Maya2011 can parent a QMainWIndow to Maya main window, making it a part of Maya UI. Focusing problem is gone.

Like versions before it, Maya2011 installation doesn’t include PyQt4. Install PyQt4 separately, and copy PyQt4 directory and SIP files to Maya2011/Python/Lib/site-packages/. The catch is the latest build of PyQt4 won’t work. Maya2011 is compiled against a specific version of Qt (4.6?). Those Qt dlls reside in Maya2011/bin/ won’t match PyQt4 compiled agianst newer versions of Qt, you will have DLL errors! And those dlls cannot be overridden, or Maya will fail to load. The matched version of PyQt4 should be PyQt-Py2.6-gpl-4.6. I found a copy for 64-bit application here. To test if it works, open mayapy.exe in Maya2011/bin/, and run:

from PyQt4 import QtCore

If no error appears, everything will be fine.

Revised: Maya2011 is compiled against Qt4.7 per doc. So I think any PyQt4 build not newer than 4.7 will do.

Leave a comment

Filed under Maya API, Python

Maya Python Access String Attribute

Maya Python API has no MString inplementation, so

a = data.inputValue(someNode.someAttr).asString()

will fail. Use a MFnStringData to extract the string carried by the attribute:

textData = data.inputValue(someNode.someAttr).data()
textFunc = OpenMaya.MFnStringData(textData)
a = textFunc.string()

where a will be a normal Python string.

Leave a comment

Filed under Maya API, Python

Extracting Mesh

This is a quick note about how to extract polygonal mesh from an object in Maya API and Max SDK.

In Maya, given the MObject of something, first we check if it is a mesh by MObject::hasFn(), if it returns true, create an MFnMesh based on this MObject. i.e.

if(some_obj.hasFn(MFn::kMesh))
{
MFnMesh fmesh(some_obj);
// do something to the fmesh
}

MFnMesh is the mesh function set can do almost anything to the mesh. For example:

int n_poly = fmesh.numPolygons();
 int n_vert = fmesh.numVertices();

tells us how many faces or vertices the mesh has.

In Max, there are two kinds of mesh object: TriObject is strictly triangular mesh, and PolyObject is polygonal mesh. Once you have the INode, you must check if it can be converted to a specific kind of mesh object. If it is convertible, the actual mesh function and data can be accessed as a specific member belongs to the object. i.e.

char retrievePolyFrom(INode * node, MNMesh& out_mesh)
{
 Object *cvtobj = node->EvalWorldState(0).obj;

 if (cvtobj->CanConvertToType(Class_ID(POLYOBJ_CLASS_ID, 0)))
 {
 PolyObject *poly = (PolyObject *)cvtobj->ConvertToType(0, Class_ID(POLYOBJ_CLASS_ID, 0));

 out_mesh = poly->mm;
 return 1;
 }
 return 0;
}

This only works with (polygonal) MNMesh, which is PolyObject::mm. The (triangular) Mesh should be TriObject::mesh.

Now you can use the function set to retrieve or modify the mesh data. i.e.

int face_count = poly_mesh.F(0)->deg;

This returns how many vertices the fist face of the polygonal mesh has.

Leave a comment

Filed under Max SDK, Maya API

Finding Materials

This note is about how to find materials in Maya API and Max SDK. In Maya, we use MItDag to walk through all meshes to look for attached materials. All types of geometry, mesh, NURBS surface, SubDiv, could have material, but for simplicity, here only meshes are listed.

MItDag itdag(MItDag::kDepthFirst, MFn::kMesh);
for
(; !itdag.isDone(); itdag.next())
{
MDagPath path;
itdag.getPath(path);

MObject omesh = itdag.currentItem();

// find the material and do something to it

}

There is no direct connection between mesh and material, there are linked up via a Shading Engine (or SG). Find the SG by MItDependencyGraph go downstream:

MObject findShadingEngine(MObject &mesh)
{
MStatus result;
MItDependencyGraph itdep(mesh, MFn::kShadingEngine, MItDependencyGraph::kDownstream, MItDependencyGraph::kDepthFirst, MItDependencyGraph::kNodeLevel, &result);
for(; !itdep.isDone(); itdep.next())
{
return itdep.currentItem();
}
return MObject::kNullObj;
}

Then go upstream from the “surfaceShader” plug of SG to find the material. Only lambert, blinn, and phong is listed here:

MObject findMaterial(MObject &mesh)
{
MObject osg = findShadingEngine(mesh);
if(osg == MObject::kNullObj)
return osg;
MStatus result;
MPlug plgsurface = MFnDependencyNode(osg).findPlug("surfaceShader");
MItDependencyGraph itdep(plgsurface, MFn::kInvalid, MItDependencyGraph::kUpstream, MItDependencyGraph::kDepthFirst, MItDependencyGraph::kNodeLevel, &result);
for(; !itdep.isDone(); itdep.next())
{
MObject ocur = itdep.currentItem();
if(ocur.hasFn(MFn::kLambert) || ocur.hasFn(MFn::kBlinn) || ocur.hasFn(MFn::kPhong))
return ocur;
}
return MObject::kNullObj;
}

Now we have the MObject pointed to the material ready for further operation.

In Max, there is nothing like MItDag, I do it myself:

void lsChildNode( INode *parent, std::vector<INode *>& nodeList)
{
int objectCount = parent->NumberOfChildren();
for(int i = 0; i < objectCount; i++)
{
INode* cur = parent->GetChildNode(i);
nodeList.push_back(cur);
lsChildNode( cur, nodeList);
}
}
void lsAllNodeInScene(std::vector<INode *>& nodeList)
{
Interface* ip = GetCOREInterface();
INode* parent = ip->GetRootNode();
int objectCount = parent->NumberOfChildren();
for(int i = 0; i < objectCount; i++)
{
INode* cur = parent->GetChildNode(i);
nodeList.push_back(cur);
lsChildNode( cur, nodeList);
}
}

It starts with scene root, lists everything under it, generating a long list of INode. Material is accessed directly by INode::GetMtl()

for(int i=0; i < allNode.size(); i++)
{
Mtl* m =  allNode[i]->GetMtl();
if(m) do_something_to(m);
}

Leave a comment

Filed under Max SDK, Maya API

API Animation Curve

This document is about how to create keyframe animation by Maya API and Max SDK.

First, some basic idea. An animation curve can be regarded as Q = f(t), means at any given time t, you can calculate the value of Q by function f. Both Maya and Max uses modified cubic 2 dimensional bezier curve as f. The curve is a number of segments connected by knots call keyframes, or keys. Below is the diagram showing one segment.

Green dotted line is the curve, which is defined by 4 points, red ones are end points, blue ones are control points. You may call those yellow lines handles. Those points live in 2-dimensional space. Actually a key should have one end point and two control points, one for in segment, the other for out. Two keys defines one segment, and a curve should have at least one segment, so at least two keys.

Creating animation curve in Maya is fairly straightforward. You have the MPlug of any keyable attribute, create the curve by MFnAnimCurve::create(MPlug), then add keys by MFnAnimCurve::addKeyframe(MTime, double). So at the end point, Q is the double value, and t is MTime. For each control points, we need tangent type, angle, weight.
Tangent is the handle mentioned before. In the following image:


Tangent angle equals atan(dQ/dt), and tangent weight is length of the handle, which equals sqrt(dQ*dQ + dt*dt).
Set the tangent angle and weight by MFnAnimCurve::setTangent().
Tangent type is changed by MFnAnimCurve::setInTangentType() and MFnAnimCurve::setOutTangentType(). I think MFnAnimCurve::kTangentFixed is the most flexible and useful.
There are two kinds of animation curve, weighted and unweighted. We are talking about weighted version here, so MFnAnimCurve::setIsWeighted(true).
Not yet, tangent handles should be unlocked by calling MFnAnimCurve::setTangentsLocked() and MFnAnimCurve::setWeightsLocked() before those angles and weights can be correctly set.

The Max side of the story is a bit more confusing. In Max, keyframe animation is done via controllers. Controllers are attached to a parameter belongs to a IParamBlock2 of the INode, or just built into the INode. There are a few ways to access the controller, i.e.
node->GetTMController()->GetPositionController()->GetXController();
Once you have the controller, get the keyframe interface by
IKeyControl *ikeys = GetKeyControlInterface(c);

Keyframe is added by IKeyControl::SetKey(). Specific controller requires specific kind of keys. We only talk about IBezFloatKey here.

It has time (t) and val (Q).

Tangent type is set by SetInTanType() and SetOutTanType(). BEZKEY_USER is similar to Maya’s Fixed type.

A Max key has intan, outtan, inLength, and outLength. The value of intan and outtan equals dQ/dt. The value of inLength and outLength equals dt/time_span. The time_span for out tangent of first key is shown below

Remember multiply any time value by TicksPerFrame.

Leave a comment

Filed under Max SDK, Maya API

Finding Stuff by MItDag

This note is about how to find stuff in Maya API.

Sometimes we need to find the specific object in the scene by its name. Here is a very crude way to do that:

MItDag itdag(MItDag::kDepthFirst); 
for(; !itdag.isDone(); itdag.next()) 
{ 
  MFnDagNode pf(itdag.currentItem()); 
  if(pf.fullPathName() == MString(name)) 
  { 
    return itdag.currentItem(); 
  } 
} 
return MObject::kNullObj;

The DAG iterator starts as the world root, the traverses through every available DAG node, comparing the pall path name of the node with given name. It returns the MObject if hits a match. For example: to find sphere1 under group1, which is under group2, we should give “|group2|group1|sphere1” to match.

It is quite a wasteful way to find a node in case there are lots of them out there, traversing to right end of them can take some time. I think the searching process can be benefit from the hierarchical nature of the notes. In other words: if |group2 does not exist, |group2|group1 and |group2|group1|sphere1, or everything possibly under |group2 won’t exist either. So we search |group2 first. If found, reset the iterator to |group2, looking for |group2|group1. If found, reset to where we found and search for the next one under it. Anytime the search returns nothing, time to abort. The improved method is:

char findChildByName(MObject &parent, MObject &result, std::string &name)
{
 MItDag itdag(MItDag::kBreadthFirst);
 
 if(parent != MObject::kNullObj)
   itdag.reset(parent);
 
 for(; !itdag.isDone(); itdag.next())
 {
   MFnDagNode pf(itdag.currentItem());
   if(pf.fullPathName() == MString(name.c_str())) 
   {
     result = itdag.currentItem();
     return 1;
   }
 }
 result = MObject::kNullObj;
 return 0;
}

char AHelper::getObjByFullName(const char* name, MObject& res)
{
 std::string r;
 std::string str = name;
 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
 boost::char_separator<char> sep("|");
 tokenizer tokens(str, sep);
 MObject parent = MObject::kNullObj;
 for (tokenizer::iterator tok_iter = tokens.begin();
 tok_iter != tokens.end(); ++tok_iter)
 {
   r = r + "|" + (*tok_iter);
   if(!findChildByName( parent, res, r))
   return 0;
 
   parent = res;
 }
 return 1;
}

The traversal type of the iterator is set to breadth-first, so it will jump to next sibling instead of wasting time on the children of an unmatched node.

Leave a comment

Filed under Maya API