AXE Thread draws a continuous line-of-action curve through a chain of rig controls in VP2 — no geometry, no deformers, no scene clutter. Select controls in order, click Add Thread, and a live colour-coded arc follows the chain as you animate.
For each control in the selection, an axeThreadShape C++ node is created as a direct child under that control's transform (not a sibling shape — child transform). This means each sphere node lives in the control's local space and its world-space pivot position is trivially available to the draw override every frame. The shape stores its chain identity (chainId, chainIndex, chainCount) and a minimal pointData float array.
In its prepareForDraw call, each sphere node reads its own world-space rotatePivot position and writes it to a Maya optionVar keyed by chainId and chainIndex. These optionVars are the shared memory bus between the sphere nodes and the curve node — no DG connections carrying positional data, just fast key-value reads and writes.
A single axeThreadShape in "curve mode" (chainIndex = -1) is created under the hidden AXE_thread_root transform. In its prepareForDraw, it reads all the sphere positions for its chain out of the optionVars and builds a smooth interpolated line through them. The line colour at each segment is computed from the bend angle between adjacent controls — blue for straight, through orange, to hot-pink for sharp bends.
Each sphere's .message attribute is connected to the curve node's .sphereMessages[i] array. When a control moves, VP2 marks the sphere shape dirty. That dirty state flows through the message connection to the curve node, marking it dirty in the same evaluation frame. VP2 then redraws the curve node immediately — no scriptJobs, no dgdirty calls, no per-frame polling. The curve always shows the current pose with zero latency.
All nodes in a chain share a chainId string (derived from the first control's short name, e.g. spine_C0_ctl_thread). The CHAIN_TAG attribute on each node stores this ID and is used by the Python layer to find, list, and remove chains without any additional tracking structures. The entire chain state lives in the scene's node graph.
axeThreadShape nodes and the curve node under AXE_thread_root. The rig is untouched. Build and remove are both fully undoable.The thread line colour is computed in the C++ draw override from the bend angle between adjacent controls in the chain. It reads the arc as a continuous gradient — at a glance you can see where the spine is straight, where it's easing, and where it's buckling.
| Control | What it does |
|---|---|
| Active Threads list | Displays all axeThreadChain-tagged nodes in the current scene, grouped by chain ID. Each entry shows the chain name and control count. The list is populated by scanning for axeThreadShape nodes — it reflects the live scene state. Use Refresh to resync after external scene changes. |
| Refresh | Rescans the scene for axeThreadShape nodes and rebuilds the list. Useful after opening a saved scene, undoing a build, or any operation that adds or removes thread nodes outside the UI. |
| Remove | Removes the thread chain selected in the list. Deletes all sphere shapes under each control in the chain, then deletes the curve node from AXE_thread_root. Fully undoable. Does not affect the rig controls themselves. |
| Thread Size slider | Adjusts the visual weight of all threads in the scene simultaneously. Range 0.01–1.0 (displayed as 0.00–1.00). Drives lineWidth on all curve nodes — larger values give thicker, more readable lines. Updates live as you drag. Does not have a per-chain control; it's a global scene-wide setting. |
| Add Thread | Primary action. Builds a new thread chain from the current Maya selection. Controls are used in the order they appear in the selection (the order you clicked them). Requires at least 2 controls. Requires a valid license — the button silently re-validates the stored key before proceeding. |
| License section | Token entry and validation UI. Enter your key from Long Winter Members → My Account → Maya Tool Licenses. The token is cached to disk after a successful validation; subsequent launches validate silently in the background using the stored key without blocking the UI. |
A single thread chain creates the following nodes in the scene. All are tagged with the axeThreadChain attribute for reliable lookup and removal.
spine_C0_ctl_axeThreadShapespine_C1_ctl_axeThreadShapechainIndex = 1. Inherits its control's world matrix — moves with the control automatically, no connections needed.axeThread_curve_1chainIndex = -1 (curve mode). Parented under AXE_thread_root, hidden in outliner. Reads sphere positions from optionVars and draws the interpolated coloured line. Connected to all sphere .message attrs for dirty propagation.AXE_thread_rootaxeThreadChain string tag) but the dirty propagation relies on the message connections between sphere shapes and the curve node. Breaking those connections will cause the curve to stop updating when controls move.AXE_thread_root manually. Deleting it will orphan all curve nodes if Maya's undo history is not intact. Use Remove in the UI or the Python API — these find and clean up curve nodes by chain ID before removing the root if it becomes empty.This should not happen under normal conditions — the dirty propagation via message connections is designed to update the curve node in the same evaluation frame as the sphere nodes. If you're seeing lag, a likely cause is that one or more message connections between sphere shapes and the curve node have been broken.
Check via the Node Editor: open the curve node (axeThread_curve_#) and inspect the sphereMessages array. All indices from 0 to chainCount-1 should have incoming connections from their respective sphere shapes. If any are missing, rebuild the chain (Remove + Add Thread).
Another rare cause: if VP2 is in a degraded state (many overrides active, complex scenes), the dirty flush may be deferred. Toggle the renderer or force a refresh: cmds.refresh(force=True).
The curve reads its positions from optionVars written by the sphere nodes' rotatePivot world position. If the thread looks offset, either the sphere nodes were built when the rig was in a different pose, or the rotatePivot of the control has a non-default offset.
Most mGear controls have their rotate pivot at the origin of the local transform, so this is rarely an issue. If you're using custom rig controls with an intentionally offset pivot, the thread will draw from that pivot point — which may not be the visual centre of the control.
Fix: remove and rebuild the thread with the rig in a neutral bind pose.
In order:
VP2 must be active. The C++ draw override only runs under Viewport 2.0. Switch via Renderer → Viewport 2.0 in the panel menu.
Plugin not loaded. Without axeThreadPlugin.mll, the node type doesn't exist and creation silently fails. Check the Script Editor for a warning like [AXE Thread] Plugin 'axeThreadPlugin' could not be loaded. Ensure the axeThread.mod file is in Maya's modules directory.
License not validated. The Add Thread button checks the license before calling create_thread(). If the license check fails, the build is skipped with a warning in the Script Editor. Enter your key in the License section of the UI.
Only one control selected. The chain requires at least 2 controls. Single-control selections are rejected with a warning.
Thread nodes are stored in the Maya scene file and survive save/open — they are ordinary DG nodes with custom node types, not transient objects. If the list is empty after reopening, the most likely cause is that the axeThreadPlugin.mll was not loaded when the scene was opened, so Maya couldn't recognise the node type and may have stripped or deferred the nodes.
Ensure the plugin loads automatically via the axeThread.mod file (which sets up the auto-load path). Once the plugin is loaded, click Refresh in the UI to rescan the scene — if the nodes are present, they'll appear.
If the nodes genuinely didn't survive the save, check if the scene was saved in ASCII (.ma) mode — sometimes studio pipeline tools strip unknown nodes on save. Binary (.mb) scenes are generally safer for custom node types.
The chain ID is generated from the first control's short name, with namespace and DAG path stripped, plus a _thread suffix. For example, selecting char_rig_RN:spine_C0_ctl as the first control produces a chain ID of spine_C0_ctl_thread.
If you selected a control with a very long or oddly formatted short name (e.g. an auto-generated node), the chain ID will reflect that. This is cosmetic only — it doesn't affect functionality. You can't rename chains post-build in the current version.
Yes — each Add Thread call creates an independent chain with its own unique chain ID, its own sphere nodes, and its own curve node. You can have as many chains as you need (spine, arms, legs, tail, etc.) and they all live in the scene simultaneously.
All chains are listed in the Active Threads list. The Thread Size slider affects all chains at once (it's a global scene setting), but each chain's curve node has independent connections and dirty propagation.
Not directly in the current version. Chains are built as atomic units — the sphere count, chain indices, and message connections are all set at build time and the curve node expects exactly chainCount sphere positions in the optionVar slots.
To modify a chain, remove it entirely and rebuild with the updated control list. Build and remove are both undoable, so you can iterate quickly.
Yes. Sphere nodes are built as direct children of the control transforms. For referenced rigs this means the sphere nodes are created in the referencing scene, not inside the rig file — exactly the same pattern as AXE Proxy. The nodes live in the top-level scene and reference the rig controls only through their parent transform hierarchy (not through stored paths or attribute connections that could break on namespace changes).
One important note: if the reference is removed from the scene, the sphere nodes lose their parent transforms and will error on next evaluation. Remove the thread before removing the reference.
Not through the UI currently. The size slider calls set_thread_size(chain_id, value) for every chain in the scene. For per-chain control, you can set line width directly via the Python API or the Node Editor — find the curve node (axeThread_curve_#) and adjust its lineWidth attribute directly.
The bend angle colour is computed in the C++ draw override from the angle between adjacent segment vectors. If the colour is stuck at one value, the most likely cause is that the sphere positions in the optionVars are not updating — meaning the sphere nodes' prepareForDraw isn't being called.
This typically happens when the sphere nodes are marked as not visible (their transforms or shapes may have visibility off). Check that the control transforms and all shapes under them are visible. Also confirm VP2 is the active renderer — other renderers won't call the custom draw override.
The per-frame cost is very low. Each sphere node writes 3 floats (XYZ) to an optionVar — essentially free. The curve node reads N×3 floats, fits a smooth interpolated polyline, and issues a single draw call. For a typical spine of 5–7 controls this is negligible.
The main cost is the VP2 draw call itself — but unlike AXE Onion, AXE Thread does not force viewport refreshes. It only redraws when the dirty propagation fires (i.e. when a control actually moves). During playback this is every frame; at rest it's zero cost.
With many simultaneous chains (10+) on a slow GPU, the draw calls can add up. In that case, use the display layer or visibility controls to hide threads during heavy scrubbing or final playblast.
Both create_thread and remove_thread are wrapped in undo chunks (axeThread_create and axeThread_remove) and should be fully undoable. If undo isn't working, check the Script Editor for errors during the operation — a Python exception that escapes the finally block can leave the undo chunk open.
After undoing, click Refresh in the list to rescan the scene — the list is not automatically updated by undo events.
All operations are available as clean Python calls for shelf buttons, pipeline scripts, and batch builds.
None to use the current Maya selection (in selection order). Returns a dict with spheres, curve, and chain_id. Requires 2+ controls.AXE_thread_root.size is 0.0–1.0. Drives lineWidth on the chain's curve node: maps to roughly 2–20px. To set all chains at once, iterate get_thread_shapes across all controls.axeThreadShape node paths under the given control transform. Useful for querying whether a control is already part of a chain, or for direct attribute access on the shape node.