Examples

This section provides practical code examples for working with the Hive Rigging system. These examples demonstrate common workflows and patterns used by programmers and character riggers.

Getting Started

Basic Rig Creation

The following example shows how to create a new rig instance and initialize it for use:

from zoo.libs.hive import api

# Create an empty rig instance
character = api.Rig()

# Initialize the rig with a name (creates or finds existing rig in scene)
character.startSession("myCharacter")

# The rig is now ready for component creation and manipulation

Note

The startSession() method will either create a new rig or connect to an existing rig in the current Maya scene, depending on whether a rig with the specified name already exists.

Working with Components

Component Creation and Management

Components are the building blocks of a Hive rig. Here’s how to create and manage them:

from zoo.libs.hive import api

# Initialize rig
character = api.Rig()
character.startSession("myCharacter")

# Create components (without building guides or rig yet)
godNode = character.createComponent("godnodecomponent", "godnode", "M")
leftArm = character.createComponent("vchaincomponent", "arm", "L")

# Access component registry to see all available component types
registry = character.configuration.componentRegistry()
availableComponents = registry.components  # Returns dict of available components

# Get components attached to the current rig
allComponents = character.components()  # List all components
specificComponent = character.component("arm", "L")  # Get by name and side

# Direct access using dot notation (convenient shortcut)
character.arm_L == leftArm  # True
character.godnode_M == godNode  # True

Tip

Components are identified by their type (e.g., “vchaincomponent”), name (e.g., “arm”), and side (“L”, “R”, or “M” for left, right, or middle/center).

Working with Templates

Template Loading Methods

Templates provide pre-configured rig setups that can be loaded and customized. There are two ways to work with templates:

Method 1: Create a New Rig from Template

This method creates a completely new rig instance based on a template:

from zoo.libs.hive import api

# Get the template registry
templateRegistry = api.Configuration().templateRegistry()

# Get the template path by name (as displayed in the UI)
templatePath = templateRegistry.templatePath("allosaurus")

# Create a new rig from the template
rig, createdComponents = api.commands.loadTemplate(
    templatePath,
    name="MyRigName"
)

Note

You can also provide an absolute file path to a template file instead of using the registry. This is useful for loading templates from network shares or custom locations.

Method 2: Load Template into Existing Rig

This method merges template components into an existing rig:

from zoo.libs.hive import api

# Initialize existing rig
character = api.Rig()
character.startSession("myExistingCharacter")

# Get template path
templateRegistry = api.Configuration().templateRegistry()
templatePath = templateRegistry.templatePath("allosaurus")

# Load template components into existing rig
rig, createdComponents = api.commands.loadTemplate(
    templatePath,
    rig=character
)

Warning

When loading a template into an existing rig, the existing rig’s configuration (build scripts, naming conventions, etc.) takes precedence over the template configuration. Only the components are merged to avoid conflicts.

Component Mirroring

Mirroring Components

Component mirroring creates symmetric components by duplicating and flipping existing ones. Mirroring should be performed on root components of chains (e.g., clavicle rather than arm), as the system will automatically mirror all child components.

from zoo.libs.hive import api

# Initialize rig
rig = api.Rig()
rig.startSession("myCharacter")

# Define which components to mirror (using tokenized names)
rootComponentNames = {"clavical:L"}
componentsToMirror = []

# Find components to mirror
for component in rig.iterComponents():
    if component.serializedTokenKey() in rootComponentNames:
        componentsToMirror.append({
            'side': 'R',                    # Target side for mirrored component
            'translate': ('x',),           # Mirror axis for translation
            'rotate': 'yz',                # Mirror plane for rotation
            'duplicate': True,             # Create duplicate vs. mirror in place
            'component': component
        })

# Execute mirroring
api.commands.mirrorComponents(rig, componentsToMirror)

Tip

  • translate: Specify which axis to mirror (e.g., (‘x’,) for X-axis)

  • rotate: Specify the mirror plane (e.g., ‘yz’ for YZ-plane)

  • duplicate: False mirrors in place, True creates a new mirrored component

Building and Querying Rigs

Building Guides and Rigs

Once components are created and configured, you need to build the guides and then the rig:

from zoo.libs.hive import api

# Initialize rig with components
character = api.Rig()
character.startSession("myCharacter")

# Create components (example)
arm = character.createComponent("vchaincomponent", "arm", "L")

# Build the guide layer first (creates visual guides for positioning)
character.buildGuides()

# Build the rig layer (creates actual controls and joints)
character.buildRigs()

Querying Guide Controls

After building guides, you can query and manipulate them:

# Get all guides from a component's guide layer
guideLayer = arm.guideLayer()
allGuides = guideLayer.iterGuides()

# Get a specific guide by ID (IDs are constant and should never change)
wristGuide = guideLayer.guide("end")  # Typically the wrist/end guide

# Query guide properties
wristPosition = wristGuide.translation()  # Returns api.Vector
wristRotationLocal = wristGuide.rotation(
    space=api.kTransformSpace,
    asQuaternion=False
)  # Returns api.EulerRotation

wristRotationWorld = wristGuide.rotation(
    space=api.kWorldSpace,
    asQuaternion=True
)  # Returns api.Quaternion

# Get guide data as dictionary (useful for serialization)
guideData = wristGuide.serializeFromScene()

# Get guide hierarchy
parentGuide = wristGuide.parent()  # Returns Guide instance or None

Querying Rig Controls

After building the rig, you can access the actual animation controls:

# Get rig controls from the rig layer
rigLayer = arm.rigLayer()
endControl = rigLayer.control("endfk")
uprControl = rigLayer.control("uprfk")
# or riglayer.findControls("endfk", "uprfk")
# Query control properties
controlPosition = endControl.translation()

# Access animation attributes (often on separate nodes)
# For example, IK/FK blending on an arm component
ikfkBlend = arm.controlPanel().ikfk  # Returns api.Plug instance

# Access settings nodes (hidden from animators)
settingsNodes = rigLayer.settingsNodes()  # List of api.SettingsNode

# Create custom settings
customSettings = rigLayer.createSettingsNode("myCustomSettings")
customSettings.addAttribute(
    "myHiddenSetting",
    value=10,
    default=0,
    Type=api.kMFnNumericFloat
)

Querying Deformation Joints

Access the deformation layer for skinning joints:

# Get joints from the deformation layer
endJoint = arm.deformLayer().joint("end")
jointPosition = endJoint.translation()

Accessing Meta Information

Each component and rig has associated metadata:

# Get meta nodes for debugging and advanced operations
print(character.meta)     # Rig meta node
print(arm.meta)          # Component meta node
print(rigLayer)         # Rig layer meta node

# Get the root transform of a component
rootTransform = arm.rootTransform()
print(rootTransform)

Advanced Operations

Matching Guide Transforms to Existing Joints

This technique is useful when you need to update guides to match the position of existing joints in a scene:

from zoo.libs.hive import api
from zoo.libs.maya import zapi

# Iterate through all rigs in the scene
for rig in api.iterSceneRigs():
    # Iterate through all components in each rig
    for component in rig.iterComponents():
        # Get ID mapping between guide and deform layers
        mapping = component.idMapping()
        deformLayer = component.deformLayer()
        guideLayer = component.guideLayer()

        # Get the mapping for deform layer
        deformMap = mapping[api.constants.DEFORM_LAYER_TYPE]

        # Find corresponding joints and guides
        joints = deformLayer.findJoints(*deformMap.values())
        guides = guideLayer.findGuides(*deformMap.keys())

        # Match guide transforms to joint transforms
        for guide, joint in zip(guides, joints):
            transformMatrix = joint.transformationMatrix()
            # Preserve guide scale while matching position/rotation
            transformMatrix.setScale(
                guide.scale(zapi.kTransformSpace),
                zapi.kTransformSpace
            )
            guide.setWorldMatrix(transformMatrix.asMatrix())

Creating Guides Programmatically

You can create guides dynamically with custom properties:

# Define guide data
guideData = {
    "name": "godnode",
    "translate": [0.0, 10.0, 0.0],
    "rotate": [0.0, 0.0, 0.0, 1.0],  # Quaternion rotation
    "rotationOrder": 0,
    "shape": "godnode",  # Shape name from library or custom dict
    "id": "godnode",     # Internal Hive ID
    "parent": arm.guideLayer().guide("mid"),  # Parent guide instance
    "children": [],      # List of child guides
}

# Create the guide
newGuide = arm.guideLayer().createGuide(**guideData)

# Parent components together (arm driven by godnode)
arm.setParent(godNode, godNode.guideLayer().guide("godnode"))

Note

The same creation pattern applies to rigLayer, deformLayer, inputLayer, and outputLayer.

Component Settings

Query Guide Settings

To query guide settings, there are 2 ways depending on your needs: 1. Through the component Definition. This acts as an in-memory cache, it’s not live and gets updated at save time/build time. 2. Through the Scene Node. This is a live representation of the settings.

Example for component definition

# retrieve 1 setting at a time.
hasTwists = arm.definition.guideLayer.guideSetting("hasTwists").value
# retrieve multiple settings at a time
settings = arm.definition.guideLayer.guideSettings("hasBendy", "hasStretch")
hasBendy, hasTwists = settings["hasBendy"], settings["hasStretch"].value

Example for Scene Node

# retrieve 1 setting at a time.
guideLayer = arm.guideLayer()
guideSettingNode = guideLayer.guideSettings()
hasTwists = guideSettingNode.attribute("hasTwists").value()

# retrieve multiple settings at a time
settings = arm.definition.guideLayer.guideSettings("hasBendy", "hasStretch")
hasBendy, hasTwists = settings["hasBendy"], settings["hasStretch"].value

Query Anim Settings

To query anim settings, there are 2 ways depending on your needs: 1. Through the component Definition. This acts as an in-memory cache, it’s not live and gets updated at save time/build time. 2. Through the Scene Node. This is a live representation of the settings.

Anim settings can support creating multiple nodes eg. controlPanel and constants. controlPanel is a reserved name used by hive to determine which attributes are exposed to the animator. While constants or any other name is defined by the developer of the component.

Example for component definition

ikfkDefaultState = arm.definition.rigLayer.setting(api.constants.CONTROL_PANEL_TYPE, "ikfk")
ikfkDefaultState.value

Example for Scene Node

settings = arm.rigLayer().controlPanel()
attr = settings.attribute("ik") # type: zapi.Plug
attr.value()
attr.set(1)

Scene Object Integration

Casting Scene Objects to Hive Objects

Convert Maya scene objects to their Hive counterparts for programmatic access:

from zoo.libs.maya import zapi
from zoo.libs.hive import api

# Iterate through selected objects
for sceneObject in zapi.selected():
    # Get the attached component (if any)
    component = api.componentFromNode(sceneObject)

    # Get the attached rig (if any)
    rig = api.rigFromNode(sceneObject)

    print("Object: {}".format(sceneObject.name()))
    print("Component: {}".format(component))
    print("Rig: {}".format(rig))

Component Grouping

Organize components into logical groups for better management:

# Create a component group
character.createGroup("myGroup", components=character.components())

# List all group names
groupNames = character.groupNames()

# Get components in a specific group (returns generator)
groupComponents = character.iterComponentsForGroup("myGroup")

# Remove a group
character.removeGroup("myGroup")

Command Line Operations

Batch Rig Upgrading

This example demonstrates how to upgrade Hive rigs using Maya’s batch mode (mayapy), which is useful for automated pipeline workflows:

Command Line Usage

First, set up the environment and run the upgrade script:

# Windows
set MAYA_MODULE_PATH=zootoolspro/install/core/extensions/maya
set ZOO_LOG_LEVEL=DEBUG
mayapy.exe upgrade_hive_rig_cli.py --scene my_rig_scene.ma --outputPath my_rig_scene_upgraded.ma

# Linux/Mac
export MAYA_MODULE_PATH=zootoolspro/install/core/extensions/maya
export ZOO_LOG_LEVEL=DEBUG
mayapy upgrade_hive_rig_cli.py --scene my_rig_scene.ma --outputPath my_rig_scene_upgraded.ma

Upgrade Script

The complete upgrade script is available in the resources directory:

import argparse
import contextlib


def parseArguments():
    parser = argparse.ArgumentParser("Hive",
                                     description="Provides a script to upgrade a hive rig which resides within maya "
                                                 "scene")
    parser.add_argument("--outputPath", type=str, required=True, help="Output path for the upgrade rig file.")
    parser.add_argument("--rigName", type=str, required=False, help="The name of the rig in the scene, defaults to "
                                                                    "finding the first rig in the scene.")
    parser.add_argument("--scene", type=str, required=True,
                        help="The Maya scene path to load")
    return parser.parse_args()


def upgradeRig(rig):
    """Does the actual labour of rebuilding the rig, relies on the current scenes cached rig definition.

    We always go back to the guides which updates the rig definition merging new updates from the base components
    before user modification. Before we delete the control rig(maintaining joints and deformation) then we polish
    the rig for final output.

    :param rig: The Hive rig instance to upgrade.
    :type rig: :class:`api.Rig`
    """
    rig.buildGuides()
    rig.deleteRigs()
    rig.polish()


def findRigInstance(name=None):
    """Find the first occurrence of a hive rig which matches the give rig name.

    :param name: The rig name to find in the scene.
    :type name: str
    :return:
    :rtype: :class:`api.Rig`
    """
    from zoo.libs.hive import api
    if name:
        rigInstance = api.rootByRigName(name)

        if rigInstance is not None:
            r = api.Rig(meta=rigInstance)
            r.startSession()
            return r
        raise api.HiveError("No rig in the scene with name: {}".format(name))
    rigs = list(api.iterSceneRigs())
    if not rigs:
        raise api.HiveError("No rigs in scene")
    return rigs[0]


@contextlib.contextmanager
def initializeContext():
    from maya import standalone
    standalone.initialize(name="python")
    try:

        yield
    finally:
        standalone.uninitialize()


if __name__ == "__main__":
    args = parseArguments()
    rigName = args.rigName
    outputPath = args.outputPath
    sceneFile = args.scene

    with initializeContext():
        from maya import cmds

        cmds.loadPlugin("zootools.py")

        from zoo.libs.maya.utils import files

        cmds.file(sceneFile, force=True, options="v=0;", ignoreVersion=True, open=True)
        rigInstance = findRigInstance(rigName)
        upgradeRig(rigInstance)
        files.saveScene(outputPath)

Note

The upgrade process: 1. Rebuilds guides from the current rig definition 2. Deletes the existing control rig (maintaining joints and deformation) 3. Rebuilds the rig with updated component definitions 4. Applies polish for final output