V0.2 · C++ VP2
Viewport-native rig picker for Maya 2024–2026 · Long Winter Studios

How It Works

AXE Proxy builds invisible clickable outlines over your rig controls directly in VP2. Animators hover to reveal them, click to select — the underlying rig control gets selected transparently. No rig modifications, no extra transforms, no scene clutter that survives the session.

1
Curve Sampling

For each selected control, AXE Proxy reads the NURBS curve shapes under that transform. If the control has multiple curves, the one with the largest bounding box is selected as the primary shape. The curve's CVs are sampled in local space into a point array.

2
Color Detection

The control's viewport draw color is read from Maya's override system. The color is mildly boosted (factor 1.1) to ensure the proxy outline reads clearly over the curve it mirrors. Controls with no color override fall back to white.

3
C++ axeProxyShape Node

A custom axeProxyShape C++ node (MPxLocatorNode with a DrawOverride) is created as a sibling shape under the control's transform — not a child transform. This is the same DAG parent, so the proxy inherits the control's world matrix automatically with zero rig overhead. The point data, color, line width, and target control path are stored as attributes directly on the node.

4
Display Layer Assignment

All proxy shapes are added to the AXE_ProxyLayer display layer (created automatically, type Normal). This lets you show/hide all proxies in a single click from the Layer Editor, and keeps them isolated from the rest of the scene's display logic.

5
Hover Tracking

A Qt event filter (hover_tracker) watches MouseMove events on the VP2 viewport widget and writes the cursor position (X, Y in screen pixels) to optionVars at ~30fps. The C++ draw override reads these optionVars each frame to compute whether the cursor is within the proximity radius of each proxy, and adjusts draw opacity accordingly.

6
Selection Redirect

A Maya scriptJob fires on every SelectionChanged event. If the newly-selected nodes contain any axeProxyShape parents, the job transparently replaces them with their target rig controls. The animator never sees the proxy selected — they always end up with the real control. The redirect only fires for viewport interactions, not Graph Editor or Outliner selections.

Non-destructive by design. The proxy shape is a sibling of the control's existing curve shapes — it doesn't replace, parent under, or modify any rig nodes. Removing all proxies leaves the rig exactly as it was. The only scene modification is the AXE_ProxyLayer display layer and the axeProxy_* object sets, both of which are cleaned up on Remove All.

UI Reference

Control What it does
▶ Build Selected Builds proxy shapes for the current Maya selection. Only transforms with NURBS curve shapes are processed — mesh controls, joints, and non-curve shapes are silently skipped. Already-proxied controls are also skipped (use Rebuild for those). Reports Built / Skipped / Failed counts via Maya's viewport toast.
↺ Rebuild Selected Removes existing proxies from the selected controls then rebuilds them fresh. Use after editing the rig's curve shapes or after a scale/position change that has made the proxy shape stale.
✕ Remove All Proxies Deletes all axeProxyShape nodes from the scene, restores NURBS curve visibility, removes the axeProxy_Master set, and cleans up the AXE_ProxyLayer display layer if empty. The rig is left untouched. Fully undoable.
Sets panel See the Sets section below.
Nurbs Curves — Show / Hide Toggles visibility on the NURBS curve shapes of every proxied control only — it does not touch controls without proxies. Use Hide after building to get a clean viewport showing only the proxy outlines.
Line Width slider Sets the pixel width of all proxy shape outlines globally. Range 0.5–8.0px. The label updates live while dragging; the Maya call and undo chunk are written only on mouse release to keep the undo queue clean. Persists via optionVar so new proxies built after the change will inherit the last-used value.
X-Ray toggle When ON, proxy outlines draw through geometry (depth test disabled) — useful for controls that are buried inside a mesh, like spine or hip controls on a skinned character. When OFF, outlines are depth-tested and will be occluded by geometry in front of them. Persists via optionVar.
Proximity slider The screen-space radius (in pixels) within which the cursor must be for a proxy to become visible. At 0 the proxies are always visible (proximity off). When Interaction is enabled, the default snaps to 150px on activation. Drag to taste — smaller values require more precise hovering, larger values make larger click targets. Persists via optionVar.
Enable / Disable (Interaction) Toggles the selection redirect scriptJob and the hover event filter on and off. When Disabled: proxies are still visible but clicking them selects the proxy transform, not the control. Proximity is reset to 0 (always visible) so you can inspect the proxies. When Enabled: clicking a proxy transparently selects the underlying rig control, and proximity is restored to the last-used value.

Display Settings

The three display controls — Line Width, X-Ray, and Proximity — all work the same way architecturally: they write to a Maya optionVar and then iterate over every axeProxyShape in the scene to apply the value. New proxies built after a change inherit the stored optionVar value automatically, so you don't need to re-apply settings after adding controls.

Setting OptionVar key Default Notes
Line Width axeProxyLineWidth 2.0 px Clamped to 0.5–8.0. Controls edge thickness in both normal and X-Ray modes.
X-Ray Mode axeProxyXrayMode Off Boolean. Best used on buried controls — spine, pelvis, COG, etc.
Proximity Radius axeProxyProximityRadius 0 (off) Pixel radius in screen space. 0 = always visible. Set to 150 on Interaction Enable.
Display settings persist across Maya sessions via optionVars. When you re-open Maya and relaunch the tool, your last-used Line Width, X-Ray, and Proximity values will be restored automatically on the next build.
Controls not selectable after install? Check that AXE_ProxyLayer is set to Normal display type, not Reference or Template. Fix with: cmds.setAttr("AXE_ProxyLayer.displayType", 0)

Sets

Named sets let you group controls for quick recall and targeted rebuilds. They're stored as Maya objectSet nodes tagged with an axeProxySet attribute — so they survive save/open cycles and work correctly with referenced rigs.

Action What it does
Create (with name) Type a name in the text field and click Create with controls selected. Creates a Maya objectSet named axeProxy_YourName. If the set already exists, new controls are added without duplicating existing ones.
Master set Created automatically after every Build. Contains every control that was built successfully in that operation. If the Master set already exists, new controls are merged in (deduplicated). Useful for rebuilding the entire rig's proxies in one click.
Select from dropdown Selects all controls in the chosen set. Resolves referenced nodes gracefully — if a control was renamed or its namespace changed, the tool attempts short-name and wildcard matching before giving up.
Build from set Selects the set's controls then calls Build Selected. Efficient for per-region rebuilds (e.g. rebuild just the Face set after tweaking face curves).
Suggested set naming convention: keep names short and anatomical — Body, Face, Hand_L, Hand_R, Fingers_L. Spaces are converted to underscores internally. Sets are sorted alphabetically in the dropdown.

Interaction System

Two lightweight components handle the interactive behaviour. Both are intentionally narrow in scope — they do the minimum necessary and get out of the way.

HVR
Hover Tracker (hover_tracker.py)

A Qt application-level event filter installed on Maya's viewport widget classes (QmayaGLWidget, QOpenGLWidget, etc). On every MouseMove event inside a viewport, it writes the cursor X/Y to optionVars axeProxyCursorX / axeProxyCursorY and requests a viewport refresh — throttled to 30fps to avoid hammering VP2. The C++ draw override reads these vars each draw call to compute hover state. The filter is stored on __main__ so it survives Python module reloads without leaking duplicate instances.

SEL
Selection Redirect (selection_redirect.py)

A single scriptJob on the SelectionChanged event. When fired, it checks if the focused panel is a model panel — if not (e.g. Graph Editor, Outliner, Channel Box), it exits immediately without touching selection. For viewport selections, it walks the selected transforms, checks each for an axeProxyShape sibling, reads the stored targetControl attribute, resolves the path (handling namespace changes and renames), and re-selects the final control list. A _redirecting guard flag prevents the re-select from triggering a recursive callback loop.

Interaction state auto-restores on scene open. When you open a scene that contains proxy shapes, the tool detects them on launch and automatically re-enables the hover tracker and selection redirect. You don't need to manually re-enable interaction each time.

FAQ

Build Selected reports all Skipped — nothing gets built

Two common causes:

No NURBS curve shapes. Only controls with NURBS curve shapes are buildable. If your rig uses mesh controls, joint-only pickers, or locators, those will always be skipped. Check via Outliner — expand the control's transform and look for a nurbsCurve shape node.

Already proxied. Controls that already have an axeProxyShape node are skipped on Build. If you want to refresh them, use Rebuild Selected instead.

A third case: the curve is degenerate — all CVs coincide at a single point (e.g. a collapsed curve used as an annotation or tag). The tool checks for this and skips it. Fix the curve in the rig file if you need that control proxied.

The proxy outline is in the wrong place / offset from the control

The proxy shape is a sibling shape under the control's transform. It inherits the control's world matrix automatically — so ordinarily it sits exactly on the curve. Misalignment usually means one of:

The curve has a shape-level transform offset. If a curve shape has a non-identity intermediateObject upstream, or if the CV positions are in a different space than expected, the sampled points will be wrong. This is rare on clean mGear rigs but can happen with imported or converted curves.

The rig root has been moved after proxies were built. The proxy's point data is in the control's local space, so moving the rig root doesn't misalign it — they move together. But if the control itself was repositioned after building (e.g. a guide reposition), rebuild to pick up the new CV positions.

Clicking a proxy selects the proxy transform, not the rig control

Interaction is not enabled, or the selection redirect scriptJob has died. Check the Interaction section of the UI — the status badge should read ON. If it shows OFF, click Enable.

If the badge shows ON but selection is still wrong, the scriptJob may have been killed by a Maya error or a forced scene change. Disable then re-enable Interaction to restart it.

Also confirm the panel in focus is a 3D model panel. The redirect intentionally ignores non-viewport selections — so clicking in the Outliner or Graph Editor will select the proxy node directly. This is correct behaviour.

Proxies are always visible — proximity / hover isn't working

Proximity of 0 means always visible — the hover fade is disabled. If you want hover-to-reveal behaviour, make sure the Proximity slider is set above 0 (default 150px when Interaction is enabled). If you moved it to 0 manually, drag it back up.

If proximity is above 0 but proxies are still always visible, the hover tracker may not be receiving cursor events from the viewport. This can happen if another application event filter is intercepting mouse events before they reach the viewport widget. Try disabling and re-enabling Interaction.

Proxies disappear when I hide NURBS curves

The Hide Nurbs Curves button hides curve shapes by setting visibility = False on each shape individually — not via display layer. The axeProxyShape nodes are a different shape type and are unaffected by this operation.

If your proxies are also disappearing, the most likely cause is that the display layer AXE_ProxyLayer is set to Template or Reference. In that case the layer overrides each member's visibility regardless of shape type. Fix: in the Layer Editor, right-click AXE_ProxyLayer → Attributes → set Display Type to Normal. Or via script: cmds.setAttr("AXE_ProxyLayer.displayType", 0)

The plugin warning banner appears — "Plugin not loaded — visuals disabled"

The C++ plugin axeProxyPlugin.mll is not loaded in the current Maya session. The Python side of the tool can open the UI but cannot build or display any proxies without it.

Check that the axeProxy.mod file is in a directory Maya scans for modules (usually Documents/maya/modules/ on Windows). The mod file tells Maya where to find the .mll. After ensuring the mod is in place, either restart Maya or manually load the plugin via Plug-in Manager (Windows → Settings/Preferences → Plug-in Manager → find axeProxyPlugin.mll → tick Loaded).

Does it work with referenced rigs?

Yes — with one important rule: build proxies in the referencing scene, not inside the rig file itself. Open your shot or asset scene where the rig is referenced in, select the controls normally, and build.

The tool stores control paths including namespace prefixes (e.g. char_rig_RN:spine_C0_ctl). On scene open, the selection redirect resolves these paths with a short-name fallback and a wildcard namespace search, so renamed namespaces are handled gracefully.

If controls go missing after a namespace change, the set members will still point to the old paths. Remove and re-add them to the set with the new namespace. The proxy shapes themselves can be rebuilt from the updated paths via Rebuild Selected.

Undo doesn't restore the proxy shapes after Remove All

Build and Remove are both wrapped in undo chunks (axeProxy_build and axeProxy_remove) and are fully undoable with Ctrl+Z. If undo seems to not work, check the Script Editor for errors during the operation — a failed step can leave the undo chunk open, which prevents the queue from registering correctly.

The curves visibility change (Show/Hide) is also undoable as a separate chunk. If you hid curves and then removed proxies, you may need to undo twice to restore both.

Build succeeds but I can't see any outlines in the viewport

Check in order:

VP2 must be active. The draw override only fires under Viewport 2.0. If you're running Legacy Viewport or High Quality Rendering, switch to Renderer → Viewport 2.0.

AXE_ProxyLayer visibility. The layer may be hidden. In the Layer Editor, make sure the V column is checked for AXE_ProxyLayer.

Proximity is 0 and proxies are transparent. At proximity 0, proxies are always visible at full opacity. If they're still invisible, check the line width — at 0.5px on a high-DPI display they can be nearly invisible. Bump line width up to 3–4px to confirm the shapes are actually there.

Plugin not loaded. Without the C++ plugin, the draw override doesn't run. Check for the banner at the top of the UI.

Performance drops when proximity / hover is active

The hover tracker forces a viewport refresh at up to 30fps whenever the cursor is inside a VP2 panel. This is necessary for the proximity fade to feel responsive, but it does mean VP2 is redrawing constantly while you hover — even if nothing in the scene has changed.

On complex scenes with expensive shaders or many lights this can be noticeable. Options:

Disable Interaction when not picking. The hover tracker only runs when Interaction is enabled. Toggle it off during playback review or heavy scrubbing, then back on when you need to pick controls.

Set proximity to 0. Without proximity, the draw override is a cheap static draw and no hover-driven refreshes are needed.

Use a dedicated picker viewport. Some animators open a small, isolated panel at a low resolution specifically for control picking, keeping the main viewport free for playback. Proxies only refresh the panel the cursor is in.

Can I proxy mesh controls or locators (not just NURBS curves)?

Not currently. The curve sampler specifically looks for nurbsCurve shape nodes under the control transform. Mesh controls and locators are silently skipped.

The reason is practical: NURBS curve CVs give a clean, lightweight point array that directly represents the intended control shape. Mesh controls would require edge extraction or bounding-box approximation, which is more complex and produces inferior results. This is on the roadmap but not in v0.2.

Workaround for mesh controls: parent a NURBS curve (matched to the mesh's outline) under the control transform. AXE Proxy will then build from that curve while leaving the mesh visible normally.

My settings (line width, x-ray, proximity) reset after reopening Maya

Settings are stored as Maya optionVars and persist across sessions in the Maya preferences. If they're resetting, the most likely cause is that your Maya preferences directory is being wiped or reset on launch (e.g. by a studio pipeline tool that resets prefs to a clean baseline). Check with your pipeline TD if you're in a managed environment.

You can also set defaults at the start of a session via the Python API — add the following to your userSetup.py after the tool loads:
import axe_proxy; axe_proxy.set_line_width(3.0); axe_proxy.set_proximity_radius(150)

Python API

All UI actions are thin wrappers over a clean Python API. You can use these directly from the Script Editor, shelf buttons, or pipeline scripts.

axe_proxy.build()
Build proxies for the current Maya selection. Returns a dict with built, skipped, failed lists.
BUILD
axe_proxy.rebuild()
Remove and rebuild proxies for the current selection in one call.
BUILD
axe_proxy.remove()
Remove all proxy shapes from the scene. Restores curve visibility, deletes the Master set, cleans up the display layer.
BUILD
axe_proxy.set_curves_visibility(visible)
Show or hide NURBS curves on proxied controls only. True = show, False = hide.
DISPLAY
axe_proxy.set_line_width(width)
Set global line width on all proxy shapes. Clamped to 0.5–8.0. Persists via optionVar.
DISPLAY
axe_proxy.set_xray_mode(enabled)
Toggle X-Ray (draw-through-geometry) mode globally. True = depth test disabled. Persists via optionVar.
DISPLAY
axe_proxy.set_proximity_radius(radius)
Set hover proximity radius in screen pixels. 0 = always visible. Persists via optionVar.
DISPLAY
axe_proxy.create_set("Body")
Create a named set from the current selection. Deduplicates if the set already exists.
SETS
axe_proxy.select_set("Body")
Select all controls in the named set. Handles namespace changes and renamed nodes gracefully.
SETS
axe_proxy.get_all_sets()
Returns a sorted list of set display names in the scene (e.g. ["Body", "Face", "Hand_L"]).
QUERY
axe_proxy.print_rig_map()
Prints the full proxy shape → target control mapping to the Script Editor. Useful for debugging after a namespace change or rig edit.
QUERY
Shelf button example: add this to your AXE shelf for a one-click build from selection:
import axe_proxy; axe_proxy.build()
# Full session setup — add to userSetup.py or a pipeline hook import axe_proxy # Set preferred display defaults axe_proxy.set_line_width(3.0) axe_proxy.set_xray_mode(False) axe_proxy.set_proximity_radius(150) # Build from a saved set if it exists in scene if "Body" in axe_proxy.get_all_sets(): axe_proxy.select_set("Body") axe_proxy.build() axe_proxy.set_curves_visibility(False)

Requirements

Maya Version
2024, 2025, 2026 (Windows)
Renderer
Viewport 2.0 — required for the C++ draw override
Plugin
axeProxyPlugin.mll — C++ MPxLocatorNode + DrawOverride
Python
Python 3 (bundled with Maya 2024+)
Qt Binding
PySide2 (Maya 2024) or PySide6 (Maya 2025+) — auto-detected
Control Type
NURBS curve shapes only (v0.2) — mesh controls not yet supported