Saturday, August 1, 2015

Custom MPxCommand

Wow this API stuff is really cool. As I'm picking it up it is like learning a language. I'm getting more fluent and understanding the power of it more and more. So I have written a few dependencyNodes now, so I thought I'd make a custom MPxCommand.

I built a command that searches the dependencyGraph for a given node type. You specify the node type to search for and the direction to search in. YOu then call it just as though it is another maya command. So rad as this can now be built into my code as if it were just part of maya.


So my command is: findConnectedNodeType. You call it like:

node = cmds.findConnectedNodeType('blendColors','up')

The return value is either False or the found node name. The command looks through the upstream dependencyGraph until the first instance of that node is found. It then stops the iteration and returns the node name.

Cool stuff I learnt.

To return from a MPxCommand you must use the public function of MPxCommand setResult.

self.setResult(return value goes here)

This comes at the end of the doIt() function.

Enumerators are also making a bit more sense now that I am using them more. For example I had to use MItDependencyGraph. You can specify the direction to search, up or down stream. The help docs show this for the direction.

MItDependencyGraph     (     MObject &      rootNode,
        MPlug &      rootPlug,
        MIteratorType &      infoObject,
        Direction      direction = kDownstream,
        Traversal      traversal = kDepthFirst,
        Level      level = kNodeLevel,
        MStatus *      ReturnStatus = NULL
    )

and this:

enum Direction
Direction within the DG relative to root Node or Plug.
Enumerator:
kDownstream  From source to destination.
kUpstream  From destination to source.
A bit cryptic at first if you arent familiar with how to read this. So To insert this correctly into the MItDependencyGraph class call you need to do this.

OpenMaya.MItDependencyGraph(OpenMaya.MItDependencyGraph.kDownstream)

So because this enumerator is a child of the class MItDependencyGraph , it must be specified in context of this class hierarchy. (OpenMaya.MItDependencyGraph). You can then set any number of the enum options that the help displays, in this case kDownstream or kUpstream and the class call will know to look for that enum if you set it (you don't have to specify anything and maya will use the default)

Ok. Hope that is a bit clearer than mud. Till next time.

  

Friday, July 31, 2015

Thats what lifes about....

I just met a guy, he's a barrista at a cafe at the start of willis st. He rode 5000km accross Canada. I asked if he was a cyclist? His answer was 'no, it was a drunken idea and i went through with it'. He rode Montreal to Vancouver. What an amazing trip that would have been.
If that's not living life, i dont know what is.

Thursday, July 30, 2015

Stretch Node Maya API Plugin Comparisons

As mentioned in a previous post I had some woes querying the matrix data in my plugin. I did some simplified tests and have no implemented these tests into the node. The video below demonstrates a comparison between the matrix plug node and the transform plug node.

Stretch Node Python API Plugin demo from Tim Forbes on Vimeo.

Test stretch node plugin using matrix connections rather than channel connections. This video compares two test plugins briefly. One plugin using Matrix connections, the other using Transform channel connections. This is a test purely to look at different types of data handling and how I can use Mayas API to simplify node graphs for common rigging techniques.

Wellington - our new home

Wow. What a whirlwind the last week and a half has been. We have done so much in such a short time, and we have loved every minute of it. Setting up again in a new country is always a hard task. This is our second time at it, and I do think it makes it a bit easier having done it before, but each country is different so there are always different curve balls that are sent your way.
Welly Adventures
However NZ has been really nice to us. I get this feeling, unlike in Australia, that everyone isn't out to screw you. It just seems a bit fairer here. This may be due to the smaller population, there's still a bit more personal care. I've found companies here really helpful and transparent, at least more so than their counterparts in Australia.

Anyways, we found a nice house. After being scared that we wouldn't find a dog friendly property, we really hit the rental market hard and got offered a few properties. In the end w chose the on that felt right, and in the end it ended up being perfect to split the difference between my work and Kates work! Some things just work out right.
Wellington Botanic Gardens
We are now going through the painful task of finding a used car. I hate looking for cars. Anyways, we'll get there, but it is growing tiresome sifting through all the crap on trademe.

Kate has started working and is absolutely loving it which is just the best news, and we have been catching up with good friends from Vancouver which has been really nice.

What an life change Clooney has gone through! 8 months ago he was in a cage, no where to go. Fat forward 8 months and he's getting lots of walks and now has his own Wellington registration number. We have really given him a second look at life and it is just beautiful to have him around. Although he now associated plastic bags with walks (we always grab a few to collect his business). So now every time I grab an apple from a bag, he gets all excited and thinks it's time to go and starts whining, so that lucky dog is getting walked A LOT!
Clooney
I start at Weta next Monday and am so excited. It's going to be so amazing to be part of a team that is creating such amazing work. I am feeling very inspired and pumped to create some amazing work.
Welly Adventures

Wednesday, July 29, 2015

Maya API Matrix Research

So after my last post I have delved into Maya to test out some Matrix data queries to determine where my node was going wrong. The test I set up in Maya was simple.
Build a locator. Move it somewhere other than the origin (origin is fine but the data would just read 0.0,0.0,0.0. I wanted something in there as it makes it easier to verify that the data is returning correctly.

I then started writing some API code that would:
#get the active selection
#loop through selected nodes and for each node
     #find the plug for the matrix attribute
     #get that plug as matrix data
     #extract the transformation data
     #print the x,y and z coordinates.

I chose this test as that is exactly what is going on in my stretch node. In the node the .matrix attr is connected into the node, and from that plug I try to retrieve the x,y and z position. However it is returning incorrectly.

So the code in my Maya test scene is working, so the error must be in the creation of the node attribute, or the way in which I extract the data from the data block. Anyways, here is the code I used to the the t.x,t.y and t.z of a selected object from the matrix plug. I hope you find it useful.

import maya.OpenMaya as OpenMaya
dagPathFn = OpenMaya.MDagPath()
#get active selection
mSelList = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(mSelList)
selItr = OpenMaya.MItSelectionList(mSelList)
#iterate through selection and get the world space positon from the matrix plug
while not selItr.isDone():
     selItr.getDagPath(dagPathFn)
     nObj = dagPathFn.node()
     #we got the node from the dagPathFn#now we get the dependency node
     objDepNodeFn = OpenMaya.MFnDependencyNode(nObj)
     #find the .matrix plug
     mxPlug = objDepNodeFn.findPlug('matrix')
     print mxPlug.name()
     print objDepNodeFn.name()
     #get plug as MObject which we will attach MFnMatrixData to.
     mxObj = mxPlug.asMObject()
     mxDataFn = OpenMaya.MFnMatrixData(mxObj)
     #query the transformation, returns MTransformationMatrix
     trfnMX = mxDataFn.transformation()
     #get the translate in world space
     v = trfnMX.getTranslation(OpenMaya.MSpace.kWorld)
     #print it out!
     print v.x,v.y,v.z
     selItr.next()

Positon From Matrix

I am currently switching my stretchNode over to work on a matrix input rather than a vector input. I was having issues when querying the dataBlock of the MPxNode to return the matrix data. Pretty much I wasn't getting the correct information. The way this connection was made was the ".worldMatrix" attribute was connected to the '.inMatrix' attribute on my stretchNode. I accesed this with:

startMXDataH = block.inputValue(stretchNodeMX.aStartMX).asMatrix()

Now this was not playing nce with me. As I went further down the track I was not getting the correct information returned.

So here's what I have..I'll delve deeper tomorrow...

def compute(self,plug,block):
#method 1
#get matrix and translate from finding the dependency node and working from there
#returns the correct t.x,t.y and t.z
#-12.0540640814 5.6978884254 7.7806477623

sNode = self.thisMObject()
plugArray = OpenMaya.MPlugArray()
depNodeFn = OpenMaya.MFnDependencyNode(sNode)
startPlug = depNodeFn.findPlug(stretchNodeMX.aStartMX)
startPlug.connectedTo(plugArray,True,False)
stObjMx = OpenMaya.MMatrix()
if plugArray.length()>0:
for i in range(0,plugArray.length()):
stObj = plugArray[i].node()
stDagNodeFn = OpenMaya.MFnDagNode(stObj)
stObjMx = stDagNodeFn.transformationMatrix()
stP = OpenMaya.MTransformationMatrix(stObjMx)
vec = stP.getTranslation(OpenMaya.MSpace.kWorld)
print vec.x,vec.y,vec.z

#method 2
#get the matrix data from the input plug and work from there
#returns the incorrrect t.x,t.y and t.z
#5.26354424712e-315 0.0 0.0078125

startMXDataH = block.inputValue(stretchNodeMX.aStartMX)
startMX = startMXDataH.asMatrix()
#just print the "translate" parts of the matrix - does not return what I'd expect.
print startMX(0,0),startMX(0,1),startMX(0,2)

#method 3
#returns the incorrrect t.x,t.y and t.z
#0.0 5.26354424712e-315 0.0

#get the transformation matrix to query the translation from there. Once again it returns the incorrect imformation
mxData = OpenMaya.MFnMatrixData()
mxData.create(startMX)
stPTransformationMatrix = mxData.transformation()
stPTranslation = stPTransformationMatrix.getTranslation(OpenMaya.MSpace.kWorld)
print stPTranslation.x,stPTranslation.y,stPTranslation.z

block.setClean(plug)

Obviously something's going screwy with my coding here, and my use of the API. But anyways, it's all about digging through it and working out whats wrong...to be continued tomorrow. Something's going wrong with either my creation of the matrix attribute and the kind of data there..but I'm pretty sure thats good. So it must be in the way I am querying the data from the dataBlock...hmmm.

TBC...

Tuesday, July 28, 2015

Stretch Node

This afternoon I have been working on a MPxNode. As mentioned in previous posts, I am planning to learn the Maya API, and as a part of my studies I am going to build some tools that will make my rigging workflow more efficient. So I have started somewhere simple. We all build stretchy joints into our characters for one reason or another. The standard setup is as such: two input points have the world position connected into a distanceBetween node. The output of the distance between node is divided by the full length of the joint chain before it starts stretching. This is normally done in a multiplyDivide node. Now if you want to build global scalability into your rig, you need to divide the initial output from the distanceBetween node by a global scale parameter. This requires another multiplyDivide node.So here we have 5 nodes to calculate something very simple. What I decided to do was build a node that would require two input points only. Therefore the setup will use 3 nodes, 2 inputs and the custom node.

The node is really very easy to write, and the longest part of it is actually setting up the attributes on the node and how they affect each other. Once you have a template for a MPxNode you can just copy and paste it as a starting point. However it is always nice to write this out and make sure you understand it rather than just copying and pasting it.

So this stretch node has 3 inputs. the startPoint and endPoint, as well as a length attribute which the user sets to the length at which stretching will start. There are then two outputs. Distance, which just returns the distance between the two in points at any given time (useful for determining the point at which stretching will occur), and the main output which outputs the final stretch value which will generally get piped into the scaleX of the necessary joints.

What awesome things did I learn when writing this plugin. The attribute that enables MPoint to return the distance between two points.

posA = OpenMaya.MVector()
posB = OpenMaya.MVector()
p0 = OpenMaya.MPoint(posA)
p1 = OpenMaya.MPoint(posB)
distance = p0.distanceTo(p1)#returns double

Next awesome thing.In the initialize() function we call this:
plugin = OpenMayaMPx.MFnPlugin(obj,'Tim Forbes','0.0','Any')
We then register the plugin type. Now this is different depending on what plugin you are creating, and will require different input data. Somewhere to get caught out, especially if you copy your template from a OpenMayaMPx.MPxCommand type plugin...like I did :p
The differences here are below:
OpenMayaMPx.MPxCommand will be plugin.registerCommand('command name', creator)
The command only requires the command name and the creator function, however the node requires a few extra things as seen below.
OpenMayaMPx.MPxCommand will be plugin.registerNode('stretchNode', stretchNode.kPluginNodeId,creator,initialize)

Because we are creating a node that will be connected to the dependency graph we need a unique node ID, we need the node name, the creator function and also the initialize function. The initialize function is where all the node attributes are added. The creator function is where our pointer is created to access the plugin within maya.
The unique Node Id is created using the inbuilt maya function: kPluginNodeId = OpenMaya.MTypeId(0x00000882)
For home use I have generated an arbitrary ID, however in production you would want to make sure you used an ID that would not clash with in house node IDs.Currently I built the node to take on the translates of the two input points. This was for ease of building and proof of concept. My next version will take wither the world matrix or transforms, thus making the node more adaptable to different rigging scenarios. Anyway, I built this node into a rig and you can view it working below.

Stretch Node from Tim Forbes on Vimeo.

Maya Python API Stretch Node


Next Up. Add in matrix connections to the StretchNode. Following that I'm going to make a MPxCommand plugin that traverses the dependency graph to find connected skin clusters. This will be useful in a convoluted node graph if you need to manupulate the skin cluster in a specific way, or really any specific node I guess:-)