Friday, 15 February 2013

MetaData....the what and the why?

Before I go and do a new set of Vimeo vid's on the MetaClass API in Red9 I thought I'd go through a few of the basics. This is kind of triggered from a few mails I've had from people asking what the concept behind the MetaData API is and how it might benefit them. MetaData is nothing more than an API, how you use it is down to you...

Part1 -The Factory:

So in a nut shell, it's a factory class with very careful over-loading of some of the key Python calls. The factory stuff really isn't that complex. It's one of those concepts that sounds hard but actually it's really simple. The idea with mine is that any Maya node with an 'mClass' attribute on it, where that attr value matches a pre-registered class, will return that class in all the get() calls in the api ..... example:

  • 'myNode' has an attribute called 'mClass' who's value is 'metaRig' 
  • 'metaRig' is a registered class that inherits from r9Meta.MetaClass 
  • metaRig class is automatically registered to the systems by an internal function that maps all inheritances from MetaClass r9Meta.registerMClassInheritanceMapping()  

The factory part of meta goes like this:

  • r9Meta.getMetaNodes() or r9Meta.getConnectedMetaNodes() will look first for nodes that have the mClass attribute on them. The search is clamped to registered nodeTypes managed by a call that can be run to register any nodeType to the systems so you can expand as needed.
  • resulting nodes are passed in to the __new__() of MetaClass 
  • the mClass attr 'metaRig' is matched to the registered class 
  • that class is instantiated and returned back to you 

So what???????? Well imagine you do getMetaNodes() on your rig scene that has an mRig node 


 Now mRig is a class object of type 'metaRig' and that is a subclass of Meta so has all the attribute handlers available. Basically that Maya node is now bound to a given class like Pymel does, you do pm.selected() on a transform you get back a pymel.Transform class object, you do the same on a light....etc. Well in the same way MetaClass does the same for any Maya nodes. More importantly by subclassing MetaClass you can bind your own classes up to your own nodes in any system.

So lets quickly look at the metaRig setup in the Red9 unitTests:

#cast the MayaNode to a metaRig object
assert mRig.L_ArmSystem.L_ArmSupport.mNode=='L_ArmSupport'
assert mRig.L_ArmSystem.L_ArmSupport.SUP_IKHandle[0]=='|World_Ctrl|L_Wrist_Ctrl|ikHandle1'

Note here that I can just use dot complete to actually walk the metaRig network. This is because by getting the attribute of type message the code looks at the linked nodes, if they too have the mClass attr they are returned as python metaClass objects too, so you can just keep walking down the tree. This makes finding nodes in complex systems like rigs an absolute doddle.

Part2 -Attribute Handlers:

So above we saw that metaClass gives you the ability to turn any Maya node into a class bound to that node, so what? Well in the rigs case it means that we can get data dead easily. This is because I've also very carefully managed all attribute calls via the MetaClass api. In doing the mRig.L_ArmSystem call above the code first tests the type of attribute, in this case a message, and uses the listConnections call to get back the data.

The attribute handlers in Meta seamlessly deal with getAttr, setAttr and addAttr, in fact addAttr manages all the flags for you, you can even add the attr without telling it the type, I derive it from the value you pass in if I can. Also you can pass ANY node into the MetaClass api, the node doesn't have to take advantage of the factory stuff, you can just use it as an attribute handler if you like. 
Hint: When a node is passed into MetaClass all attributes show on the dot-compete in the script editor ;).

Currently I support:

  • floats 
  • doubles
  • ints
  • bools
  • strings    ( automatically serializes and deserializes to JSON if the value is complex)
  • enums    ( allows you to set the data via the int or string representation )
  • message ( get runs listConnections, returning mClass objects if found, set will automatically connect)
  • doubleArrays (sets the structures up for you when adding)
  • double3
  • float3 

Even better, and here's where it gets fun, if you pass a python dict, or complex structure to a string attr, it's serialized and stored via JSON for you. Here at work we store our zero pose, generated by the
poseSaver, actually on the mRig node itself as a json string. Same way that the SceneReview serializing all the comment data from an internal dict straight to the 'time1' node in Maya. The AttrMap is also doing this when you serializeToNode.

Part3 - The Big One!

 The big one though is this..... any node in Maya that has the mClass attr, that is valid, will give you back the correct class automatically! You don't have to worry about what class to call, what function is in what class and if node==this do that, elif to this.... the node comes back as the correct Python object ready for you without any work. There is a direct correlation between the MayaNode and the class that manages it, like Pymel all the functions you may need, code in the class for that node. It keeps code clean and means that by subclassing you get all functionality for all similar node types for free, and get access to all the core stuff in Meta....which is expanding fast.

I've added a ton of hooks in there recently as I've been working with Josh Burton on the new Morpheus project. These all allow you to sub-class and register new types a lot easier. Most of the code we now write at work is based on Meta, either because it's so easy to get data and attributes back, or because we have many nodes for many reasons, facialRig, supportNodes, exportTags managing systems....

Hope that helps a little?

Have a read of the unitTests and examples folders in the Red9 pack, they're there for a reason and might shed some light on all of this ;)