Shard - Node Based Execution
Shard is largely an experimental sandbox project I have been working on at The Creative Assembly, with the original intention of experimenting with ideas of creating a rigging framework that is consistent, reliable and highly scalable.
With that said, the core of shard has nothing within it that defines it as a rigging tool. It is a pure python package, which holds the framework for a generic task execution system. Each task is exposed as a class, which can expose input and output plugs along with an execution method.
This lays the foundation for rigging-centric nodes to be developed - but the generic underlying system allows it also to be utilised for any manor of tasks - such as node-based pipelines (file operations, referencing, io tasks) etc.
Defining a Node
A node is a highly encapsulated class, which allows a developer to define expected inputs along with guaranteed outputs. This makes the process of writing rigging nodes incredibly safe, testable and straight forward - as the developer only has to worry about the building of their node and not about where the required information is coming from.
A node can also expose static data - that is, data that is not (typically) changed by other nodes within the graph, instead static data (or Options) are set up-front by the person/mechanism that is building the graph.
All inputs and outputs are Plug classes, and can be connected together to create a node graph. Graphs can range from being incredibly simple (maybe even just one node!) to massive graphs containing hundreds of nodes. This approach makes it great for development, but can become visually complex when building rigs through low-level nodes.
The entirety of the system has no UI dependency however, and therefore you can save, load and alter graphs programatically - allowing you to create compounded reference nodes that load in complete graphs and alter option data based on the reference node options you expose. This is a great way of maintaining low-level nodes whilst having higher-level references.
Graphs are a great paradigm for developing; they give great amounts of encapsulation and therefore lend themselves to easy testing. The downside is handling the visual complexity that can come with large graphs.
This is an area I am still looking at, but so far the following has helped dramatically:
Group Boxes to help categorise nodes and tie them visually together
Ability to hide unconnected plugs - removing visual clutter
Ability to hide 'minor' nodes, such as nodes that simply give access to objects within a DCC
Graph referencing to help minimise the amount of nodes that need to be on the slateDynamic display of node properties based on their type
The graph itself is created as a QGraphicsView whereby visual nodes are QGraphicsItems which hold references to the underlying node in the Shard graph which they represent. Upon selection the options exposed by the node are parsed and based on their value type the most logical QWidget is instanced to represent it. This makes for a nice, dynamic and fluid feeling UI.
Both the core of Shard as well as the UI that visualises the graphs are all written in Python, using PySide as the UI framework. The framework is written entirely standalone and has no reliance on any DCC application.
For each host integration it is then simply a case of either sub-classing the main UI widget or creating a wrapper to handle the host specific API's. Through this mechanism I have been able to implement and execute a simple graph from within Maya, Motion Builder, Modo, 3dsmax and Houdini.
The nodes themselves may or may not be application specific depending on what role they are playing. Therefore, each implementation can add custom locations for Shard to look at for nodes. A logging node for instance may be pure python and therefore sit in the base implementation whilst a node that builds a specific rig component in Maya may exist in a location that only the Maya implementation points to.
One of the biggest issues I have faced with the large majority of Modular Rigging Tools is that they give plenty of encapsulation, but creating inter-module behaviour becomes rather hacky.
Many approaches involve layered components (which only side steps the problem rather than resolves it) or a post-script approach which works but means you are constantly maintaining and writing bespoke scripts for specific rigs.
A graph alleviates this completely, as nodes are just blobs with inputs and outputs.