Skip to content

Instantly share code, notes, and snippets.

@danbradham
Last active August 29, 2015 14:15
Show Gist options
  • Save danbradham/929ab23057fbb3a01108 to your computer and use it in GitHub Desktop.
Save danbradham/929ab23057fbb3a01108 to your computer and use it in GitHub Desktop.
An arm rigging marathon.
'''
toolshed
========
Rigging tools for Autodesk Maya.
'''
from maya import cmds, OpenMaya
import maya.api.OpenMaya as mapi
import traceback
CURVE_CTRLS = {
'circle' : [2, 3, [[2.0, 0.0, 0.0], [1.4142135381698608, 0.0, 1.4142135381698608], [0.0, 0.0, 2], [-1.4142134189605713, 0.0, 1.4142134189605713], [-2, 0.0, 0.0], [-1.4142134189605713, 0.0, -1.4142134189605713], [0.0, 0.0, -2], [1.4142134189605713, 0.0, -1.4142134189605713], [2.0, 0.0, 0.0], [1.4142135381698608, 0.0, 1.4142135381698608], [0.0, 0.0, 2]], [-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]],
'double_arrow' : [0, 3, [[1.0, 0.0, -3.0], [1.0, 0.0, -3.0], [1.0, 0.0, -3.0], [1.0, 0.0, -2.0], [1.0, 0.0, -1.0], [1.0, 0.0, 0.0], [1.0, 0.0, 1.0], [1.0, 0.0, 2.0], [1.0, 0.0, 3.0], [1.0, 0.0, 3.0], [1.0, 0.0, 3.0], [2.0, 0.0, 3.0], [2.0, 0.0, 3.0], [2.0, 0.0, 3.0], [0.0, 0.0, 5.0], [0.0, 0.0, 5.0], [0.0, 0.0, 5.0], [-2.0, 0.0, 3.0], [-2.0, 0.0, 3.0], [-2.0, 0.0, 3.0], [-1.0, 0.0, 3.0], [-1.0, 0.0, 3.0], [-1.0, 0.0, 3.0], [-1.0, 0.0, 2.0], [-1.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [-1.0, 0.0, -1.0], [-1.0, 0.0, -2.0], [-1.0, 0.0, -3.0], [-1.0, 0.0, -3.0], [-1.0, 0.0, -3.0], [-2.0, 0.0, -3.0], [-2.0, 0.0, -3.0], [-2.0, 0.0, -3.0], [0.0, 0.0, -5.0], [0.0, 0.0, -5.0], [0.0, 0.0, -5.0], [2.0, 0.0, -3.0], [2.0, 0.0, -3.0], [2.0, 0.0, -3.0], [1.0, 0.0, -3.0], [1.0, 0.0, -3.0], [1.0, 0.0, -3.0]], [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42]],
'diamond' : [0, 1, [[0.0, -2.2204460492503131e-16, 1.0], [0.0, 1.0, 2.2204460492503131e-16], [1.0, 0.0, 0.0], [0.0, -2.2204460492503131e-16, 1.0], [0.0, -1.0, -2.2204460492503131e-16], [1.0, 0.0, 0.0], [0.0, 2.2204460492503131e-16, -1.0], [0.0, 1.0, 2.2204460492503131e-16], [-1.0, 0.0, 0.0], [0.0, -2.2204460492503131e-16, 1.0], [0.0, -1.0, -2.2204460492503131e-16], [-1.0, 0.0, 0.0], [0.0, 2.2204460492503131e-16, -1.0], [0.0, -1.0, -2.2204460492503131e-16]], [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]],
'arrow' : [0, 3, [[-1.0, 0.0, 2.0], [-1.0, 0.0, 2.0], [-1.0, 0.0, 2.0], [1.0, 0.0, 2.0], [1.0, 0.0, 2.0], [1.0, 0.0, 2.0], [1.0, 0.0, 1.0], [1.0, 0.0, 0.0], [1.0, 0.0, -1.0], [1.0, 0.0, -2.0], [1.0, 0.0, -2.0], [1.0, 0.0, -2.0], [2.0, 0.0, -2.0], [2.0, 0.0, -2.0], [2.0, 0.0, -2.0], [0.0, 0.0, -4.0], [0.0, 0.0, -4.0], [0.0, 0.0, -4.0], [-2.0, 0.0, -2.0], [-2.0, 0.0, -2.0], [-2.0, 0.0, -2.0], [-1.0, 0.0, -2.0], [-1.0, 0.0, -2.0], [-1.0, 0.0, -2.0], [-1.0, 0.0, -1.0], [-1.0, 0.0, 0.0], [-1.0, 0.0, 1.0], [-1.0, 0.0, 2.0], [-1.0, 0.0, 2.0], [-1.0, 0.0, 2.0]], [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]],
'square' : [0, 3, [[-1.0, 0.0, 1.0], [-1.0, 0.0, 1.0], [-1.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [-1.0, 0.0, -1.0], [-1.0, 0.0, -1.0], [-1.0, 0.0, -1.0], [0.0, 0.0, -1.0], [1.0, 0.0, -1.0], [1.0, 0.0, -1.0], [1.0, 0.0, -1.0], [1.0, 0.0, 0.0], [1.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 0.0, 1.0], [0.0, 0.0, 1.0], [-1.0, 0.0, 1.0], [-1.0, 0.0, 1.0], [-1.0, 0.0, 1.0]], [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]],
'quad_arrow' : [0, 3, [[-1.0, 0.0, -1.0], [-1.0, 0.0, -2.0], [-1.0, 0.0, -3.0], [-1.0, 0.0, -3.0], [-1.0, 0.0, -3.0], [-2.0, 0.0, -3.0], [-2.0, 0.0, -3.0], [-2.0, 0.0, -3.0], [0.0, 0.0, -5.0], [0.0, 0.0, -5.0], [0.0, 0.0, -5.0], [2.0, 0.0, -3.0], [2.0, 0.0, -3.0], [2.0, 0.0, -3.0], [1.0, 0.0, -3.0], [1.0, 0.0, -3.0], [1.0, 0.0, -3.0], [1.0, 0.0, -2.0], [1.0, 0.0, -1.0], [1.0, 0.0, -1.0], [1.0, 0.0, -1.0], [2.0, 0.0, -1.0], [3.0, 0.0, -1.0], [3.0, 0.0, -1.0], [3.0, 0.0, -1.0], [3.0, 0.0, -2.0], [3.0, 0.0, -2.0], [3.0, 0.0, -2.0], [5.0, 0.0, 0.0], [5.0, 0.0, 0.0], [5.0, 0.0, 0.0], [3.0, 0.0, 2.0], [3.0, 0.0, 2.0], [3.0, 0.0, 2.0], [3.0, 0.0, 1.0], [3.0, 0.0, 1.0], [3.0, 0.0, 1.0], [2.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 0.0, 2.0], [1.0, 0.0, 3.0], [1.0, 0.0, 3.0], [1.0, 0.0, 3.0], [2.0, 0.0, 3.0], [2.0, 0.0, 3.0], [2.0, 0.0, 3.0], [0.0, 0.0, 5.0], [0.0, 0.0, 5.0], [0.0, 0.0, 5.0], [-2.0, 0.0, 3.0], [-2.0, 0.0, 3.0], [-2.0, 0.0, 3.0], [-1.0, 0.0, 3.0], [-1.0, 0.0, 3.0], [-1.0, 0.0, 3.0], [-1.0, 0.0, 2.0], [-1.0, 0.0, 1.0], [-1.0, 0.0, 1.0], [-1.0, 0.0, 1.0], [-2.0, 0.0, 1.0], [-3.0, 0.0, 1.0], [-3.0, 0.0, 1.0], [-3.0, 0.0, 1.0], [-3.0, 0.0, 2.0], [-3.0, 0.0, 2.0], [-3.0, 0.0, 2.0], [-5.0, 0.0, 0.0], [-5.0, 0.0, 0.0], [-5.0, 0.0, 0.0], [-3.0, 0.0, -2.0], [-3.0, 0.0, -2.0], [-3.0, 0.0, -2.0], [-3.0, 0.0, -1.0], [-3.0, 0.0, -1.0], [-3.0, 0.0, -1.0], [-2.0, 0.0, -1.0], [-1.0, 0.0, -1.0], [-1.0, 0.0, -1.0], [-1.0, 0.0, -1.0]], [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80]],
'cube' : [0, 1, [[-1.0, -1.0, -1.0], [-1.0, -1.0, 1.0], [1.0, -1.0, 1.0], [1.0, -1.0, -1.0], [-1.0, -1.0, -1.0], [-1.0, 1.0, -1.0], [1.0, 1.0, -1.0], [1.0, -1.0, -1.0], [1.0, 1.0, -1.0], [1.0, 1.0, 1.0], [1.0, -1.0, 1.0], [1.0, 1.0, 1.0], [-1.0, 1.0, 1.0], [-1.0, -1.0, 1.0], [-1.0, 1.0, 1.0], [-1.0, 1.0, -1.0]], [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]],
'sphere' : [0, 1, [[0.0, 0.0, 0.99999994039535522], [0.0, 0.70710676908493042, 0.70710670948028564], [0.0, 1.0, 0.0], [0.0, 0.70710676908493042, -0.70710670948028564], [0.0, 0.0, -0.99999988079071045], [0.0, -0.70710676908493042, -0.70710670948028564], [0.0, -1.0, 0.0], [0.0, -0.70710676908493042, 0.70710670948028564], [0.0, 0.0, 0.99999994039535522], [-0.70710670948028564, 0.0, 0.70710670948028564], [-0.99999988079071045, 0.0, 0.0], [-0.70710670948028564, -0.70710676908493042, 0.0], [0.0, -1.0, 0.0], [0.70710676908493042, -0.70710676908493042, 0.0], [1.0, 0.0, 0.0], [0.70710676908493042, 0.70710676908493042, 0.0], [0.0, 1.0, 0.0], [-0.70710670948028564, 0.70710676908493042, 0.0], [-0.99999988079071045, 0.0, 0.0], [-0.70710670948028564, 0.0, -0.70710670948028564], [0.0, 0.0, -0.99999988079071045], [0.70710670948028564, 0.0, -0.70710670948028564], [1.0, 0.0, 0.0], [0.70710676908493042, 0.0, 0.70710676908493042], [0.0, 0.0, 0.99999994039535522]], [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]],
'curve_arrow' : [0, 3, [[-0.80213398918053513, 7.9291405962377267e-17, -3.1785483731729229], [-0.80213398918053513, 7.9291405962377267e-17, -3.1785483731729229], [-0.80213398918053513, 7.9291405962377267e-17, -3.1785483731729229], [0.018569192177553262, 1.2821060677180985e-16, -2.288704620441234], [0.54183430390929821, 8.7536372115188425e-17, -1.1971143864196638], [0.72153617366577438, 0.0, 0.0], [0.54183430390929821, -8.7536372115188425e-17, 1.1971143864196638], [0.018569192177553262, -1.2821060677180985e-16, 2.288704620441234], [-0.80213398918053513, -7.9291405962377267e-17, 3.1785483731729229], [-0.80213398918053513, -7.9291405962377267e-17, 3.1785483731729229], [-0.80213398918053513, -7.9291405962377267e-17, 3.1785483731729229], [-0.0014337406636666383, -5.2190130379426478e-16, 4.1752172586460139], [-0.0014337406636666383, -5.2190130379426478e-16, 4.1752172586460139], [-0.0014337406636666383, -5.2190130379426478e-16, 4.1752172586460139], [-3.0848709783395956, 7.3507530853700062e-16, 3.3447575571917558], [-3.0848709783395956, 7.3507530853700062e-16, 3.3447575571917558], [-3.0848709783395956, 7.3507530853700062e-16, 3.3447575571917558], [-2.5066285909950636, 8.6291659629758236e-16, 1.0568845692311961], [-2.5066285909950636, 8.6291659629758236e-16, 1.0568845692311961], [-2.5066285909950636, 8.6291659629758236e-16, 1.0568845692311961], [-1.7059283424781946, 4.2030669846569465e-16, 2.0535534547042875], [-1.7059283424781946, 4.2030669846569465e-16, 2.0535534547042875], [-1.7059283424781946, 4.2030669846569465e-16, 2.0535534547042875], [-1.1756995538936976, 2.315235723958968e-16, 1.4786552628151766], [-0.83763552221426174, 1.0062375886995711e-16, 0.77341543852432126], [-0.72153617366577438, 0.0, 0.0], [-0.83763552221426174, -1.0062375886995711e-16, -0.77341543852432126], [-1.1756995538936976, -2.315235723958968e-16, -1.4786552628151766], [-1.7059283424781946, -4.2030669846569465e-16, -2.0535534547042875], [-1.7059283424781946, -4.2030669846569465e-16, -2.0535534547042875], [-1.7059283424781946, -4.2030669846569465e-16, -2.0535534547042875], [-2.5066285909950636, -8.6291659629758236e-16, -1.0568845692311961], [-2.5066285909950636, -8.6291659629758236e-16, -1.0568845692311961], [-2.5066285909950636, -8.6291659629758236e-16, -1.0568845692311961], [-3.0848709783395956, -7.3507530853700062e-16, -3.3447575571917558], [-3.0848709783395956, -7.3507530853700062e-16, -3.3447575571917558], [-3.0848709783395956, -7.3507530853700062e-16, -3.3447575571917558], [-0.0014337406636666383, 5.2190130379426478e-16, -4.1752172586460139], [-0.0014337406636666383, 5.2190130379426478e-16, -4.1752172586460139], [-0.0014337406636666383, 5.2190130379426478e-16, -4.1752172586460139], [-0.80213398918053513, 7.9291405962377267e-17, -3.1785483731729229], [-0.80213398918053513, 7.9291405962377267e-17, -3.1785483731729229], [-0.80213398918053513, 7.9291405962377267e-17, -3.1785483731729229]], [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42]],
'curve_quad_arrow' : [0, 3, [[-1.0, 1.2195798144924379, -1.0], [-1.0, 1.0319021046902834, -2.0], [-1.0, 0.73962334077416081, -3.0000000000000004], [-1.0, 0.73962334077416081, -3.0000000000000004], [-1.0, 0.73962334077416081, -3.0000000000000004], [-2.0, 0.57612185552316775, -3.0000000000000004], [-2.0, 0.57612185552316775, -3.0000000000000004], [-2.0, 0.57612185552316775, -3.0000000000000004], [0.0, 0.0052882016263648239, -5.0], [0.0, 0.0052882016263648239, -5.0], [0.0, 0.0052882016263648239, -5.0], [2.0, 0.57612185552316775, -3.0000000000000004], [2.0, 0.57612185552316775, -3.0000000000000004], [2.0, 0.57612185552316775, -3.0000000000000004], [1.0, 0.73962334077416081, -3.0000000000000004], [1.0, 0.73962334077416081, -3.0000000000000004], [1.0, 0.73962334077416081, -3.0000000000000004], [1.0, 1.0319021046902834, -2.0], [1.0, 1.2195798144924379, -1.0], [1.0, 1.2195798144924379, -1.0], [1.0, 1.2195798144924379, -1.0], [2.0, 1.0319021046902834, -1.0], [3.0000000000000004, 0.73962334077416081, -1.0], [3.0000000000000004, 0.73962334077416081, -1.0], [3.0000000000000004, 0.73962334077416081, -1.0], [3.0000000000000004, 0.57612185552316775, -2.0], [3.0000000000000004, 0.57612185552316775, -2.0], [3.0000000000000004, 0.57612185552316775, -2.0], [5.0, 0.0052882016263648239, 0.0], [5.0, 0.0052882016263648239, 0.0], [5.0, 0.0052882016263648239, 0.0], [3.0000000000000004, 0.57612185552316775, 2.0], [3.0000000000000004, 0.57612185552316775, 2.0], [3.0000000000000004, 0.57612185552316775, 2.0], [3.0000000000000004, 0.73962334077416081, 1.0], [3.0000000000000004, 0.73962334077416081, 1.0], [3.0000000000000004, 0.73962334077416081, 1.0], [2.0, 1.0319021046902834, 1.0], [1.0, 1.2195798144924379, 1.0], [1.0, 1.2195798144924379, 1.0], [1.0, 1.2195798144924379, 1.0], [1.0, 1.0319021046902834, 2.0], [1.0, 0.73962334077416081, 3.0000000000000004], [1.0, 0.73962334077416081, 3.0000000000000004], [1.0, 0.73962334077416081, 3.0000000000000004], [2.0, 0.57612185552316775, 3.0000000000000004], [2.0, 0.57612185552316775, 3.0000000000000004], [2.0, 0.57612185552316775, 3.0000000000000004], [0.0, 0.0052882016263648239, 5.0], [0.0, 0.0052882016263648239, 5.0], [0.0, 0.0052882016263648239, 5.0], [-2.0, 0.57612185552316775, 3.0000000000000004], [-2.0, 0.57612185552316775, 3.0000000000000004], [-2.0, 0.57612185552316775, 3.0000000000000004], [-1.0, 0.73962334077416081, 3.0000000000000004], [-1.0, 0.73962334077416081, 3.0000000000000004], [-1.0, 0.73962334077416081, 3.0000000000000004], [-1.0, 1.0319021046902834, 2.0], [-1.0, 1.2195798144924379, 1.0], [-1.0, 1.2195798144924379, 1.0], [-1.0, 1.2195798144924379, 1.0], [-2.0, 1.0319021046902834, 1.0], [-3.0000000000000004, 0.73962334077416081, 1.0], [-3.0000000000000004, 0.73962334077416081, 1.0], [-3.0000000000000004, 0.73962334077416081, 1.0], [-3.0000000000000004, 0.57612185552316775, 2.0], [-3.0000000000000004, 0.57612185552316775, 2.0], [-3.0000000000000004, 0.57612185552316775, 2.0], [-5.0, 0.0052882016263648239, 0.0], [-5.0, 0.0052882016263648239, 0.0], [-5.0, 0.0052882016263648239, 0.0], [-3.0000000000000004, 0.57612185552316775, -2.0], [-3.0000000000000004, 0.57612185552316775, -2.0], [-3.0000000000000004, 0.57612185552316775, -2.0], [-3.0000000000000004, 0.73962334077416081, -1.0], [-3.0000000000000004, 0.73962334077416081, -1.0], [-3.0000000000000004, 0.73962334077416081, -1.0], [-2.0, 1.0319021046902834, -1.0], [-1.0, 1.2195798144924379, -1.0], [-1.0, 1.2195798144924379, -1.0], [-1.0, 1.2195798144924379, -1.0]], [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80]],
'curve_double_arrow' : [0, 3, [[1.0, 0.84919716903699882, -3.0000000000000004], [1.0, 0.84919716903699882, -3.0000000000000004], [1.0, 0.84919716903699882, -3.0000000000000004], [1.0, 1.1847764905703249, -2.0], [1.0, 1.4002583055283548, -1.0], [1.0, 1.4745123954786876, 0.0], [1.0, 1.4002583055283548, 1.0], [1.0, 1.1847764905703249, 2.0], [1.0, 0.84919716903699882, 3.0000000000000004], [1.0, 0.84919716903699882, 3.0000000000000004], [1.0, 0.84919716903699882, 3.0000000000000004], [2.0, 0.66147324152660014, 3.0000000000000004], [2.0, 0.66147324152660014, 3.0000000000000004], [2.0, 0.66147324152660014, 3.0000000000000004], [0.0, 0.0060716389043447977, 5.0], [0.0, 0.0060716389043447977, 5.0], [0.0, 0.0060716389043447977, 5.0], [-2.0, 0.66147324152660014, 3.0000000000000004], [-2.0, 0.66147324152660014, 3.0000000000000004], [-2.0, 0.66147324152660014, 3.0000000000000004], [-1.0, 0.84919716903699882, 3.0000000000000004], [-1.0, 0.84919716903699882, 3.0000000000000004], [-1.0, 0.84919716903699882, 3.0000000000000004], [-1.0, 1.1847764905703249, 2.0], [-1.0, 1.4002583055283548, 1.0], [-1.0, 1.4745123954786876, 0.0], [-1.0, 1.4002583055283548, -1.0], [-1.0, 1.1847764905703249, -2.0], [-1.0, 0.84919716903699882, -3.0000000000000004], [-1.0, 0.84919716903699882, -3.0000000000000004], [-1.0, 0.84919716903699882, -3.0000000000000004], [-2.0, 0.66147324152660014, -3.0000000000000004], [-2.0, 0.66147324152660014, -3.0000000000000004], [-2.0, 0.66147324152660014, -3.0000000000000004], [0.0, 0.0060716389043447977, -5.0], [0.0, 0.0060716389043447977, -5.0], [0.0, 0.0060716389043447977, -5.0], [2.0, 0.66147324152660014, -3.0000000000000004], [2.0, 0.66147324152660014, -3.0000000000000004], [2.0, 0.66147324152660014, -3.0000000000000004], [1.0, 0.84919716903699882, -3.0000000000000004], [1.0, 0.84919716903699882, -3.0000000000000004], [1.0, 0.84919716903699882, -3.0000000000000004]], [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42]]
}
def create_follicle(name):
'''Create a follicle.
:param name: Name of the follicle'''
shape = cmds.createNode('follicle')
transform = cmds.listRelatives(shape, parent=True)[0]
transform = cmds.rename(transform, name)
shape = cmds.listRelatives(transform, shapes=True)[0]
cmds.connectAttr(shape + '.outRotate', transform + '.rotate')
cmds.connectAttr(shape + '.outTranslate', transform + '.translate')
return transform, shape
def get_shape(transform):
'''Get the first non-intermediate shape under a transform.
:param transform: The transform to query
'''
return cmds.listRelatives(transform, shapes=True, noIntermediate=True)[0]
def get_surface_type(surface):
'''Wrapper around cmds.nodeType, returns a str instead of unicode'''
return str(cmds.nodeType(surface))
def rivet(name, surface, u, v):
'''Create a rivet.
:param name: Name of the rivet
:param surface: transform, mesh or nurbsSurface
:param u: U parameter of rivet
:param v: V parameter of rivet
:returns: transform and shape of a follicle attached to the given surface
'''
transform, shape = create_follicle(name)
surface_type = get_surface_type(surface)
if surface_type == 'transform':
surface = get_shape(surface)
surface_type = get_surface_type(surface)
if surface_type == 'mesh':
cmds.connectAttr(surface + '.worldMesh', shape + '.inputMesh')
cmds.connectAttr(surface + '.worldMatrix', shape + '.inputWorldMatrix')
elif surface_type == 'nurbsSurface':
cmds.connectAttr(surface + '.worldSpace', shape + '.inputSurface')
else:
raise RuntimeError('Incorrect surface type: ' + surface_type)
cmds.setAttr(shape + '.parameterU', u)
cmds.setAttr(shape + '.parameterV', v)
return transform, shape
def multi_rivet(nodes, surface, constrain=False):
'''Rivet a bunch of nodes to a surface.
:param nodes: List of nodes to rivet
:param surface: transform, mesh, or surface
:param constraint: use a parentConstraint instead of direct parenting
'''
rivets = []
for node in nodes:
rivet_name = node.split('|')[-1] + '_RIV'
position = cmds.xform(node, q=True, ws=True, rp=True)
u, v = get_closest_uv(position, surface)
transform, shape = rivet(rivet_name, surface, u, v)
if not constrain:
cmds.parent(node, transform)
else:
cmds.parentConstraint(transform, node, maintainOffset=True)
rivets.append(transform)
clean_up() # Remove closest point nodes
return rivets
def get_closest_uv(position, surface):
'''Get the closest uv point from a worldspace position, on a mesh or
nurbsSurface.
:param position: worldspace position
:param surface: nurbsSurface, mesh, or transform
'''
surface_type = get_surface_type(surface)
if surface_type == 'transform':
surface = get_shape(surface)
surface_type = get_surface_type(surface)
if surface_type == 'mesh':
max_v = 1
closest = 'closest_on_mesh'
if not cmds.objExists(closest):
cmds.createNode('closestPointOnMesh', name=closest)
if not surface in cmds.listConnections(closest, shapes=True):
cmds.connectAttr(
surface + '.worldMesh',
closest + '.inputMesh',
force=True)
cmds.connectAttr(
surface + '.worldMatrix',
closest + '.inputMatrix',
force=True)
elif surface_type == 'nurbsSurface':
max_v = cmds.getAttr(surface + '.spansV')
closest = 'closest_on_surface'
if not cmds.objExists(closest):
cmds.createNode('closestPointOnSurface', name=closest)
try:
cmds.connectAttr(
surface + '.worldSpace',
closest + '.inputSurface',
force=True)
except RuntimeError:
pass
cmds.setAttr(closest + '.inPosition', *position)
u = cmds.getAttr(closest + '.parameterU')
v = cmds.getAttr(closest + '.parameterV') / max_v
return u, v
def clean_up():
'''Removes closest point nodes that maye have been created.'''
nodes = ['closest_on_mesh', 'closest_on_surface']
for node in nodes:
if cmds.objExists(node):
cmds.delete(node)
def rivet_selected():
'''Rivet selected items to a surface. Selected the rivet surface laster.'''
cmds.undoInfo(openChunk=True)
sel = cmds.ls(sl=True, long=True)
multi_rivet(sel[:-1], sel[-1], constrain=True)
cmds.undoInfo(closeChunk=True)
def insert_parent(node, suffix="_GRP"):
'''Insert a group above each node.
:param nodes: A list of all nodes to insert a group above.
:param suffix: Suffix to append to group name.
:returns: Long name of inserted parent
'''
ws_matrix = cmds.xform(node, q=True, ws=True, matrix=True)
p = cmds.group(name = node + suffix, em=True)
cmds.xform(p, ws=True, matrix=ws_matrix)
#Insert parent
current_p = cmds.listRelatives(node, parent=True)
if current_p:
cmds.parent(p, current_p)
cmds.parent(node, p)
return cmds.ls(p, long=True)[0]
def insert_joint_chain(base, end, num_joints=1):
'''Insert a joint chain beneath a joint.
:param base: Base joint to insert under
:param end: End joint to reparent under new chain
:param num_joints: Number of joints to insert (default=1)
'''
base_matrix = mapi.MMatrix(cmds.xform(base, q=True, ws=True, matrix=True))
base_vect = mapi.MVector(cmds.xform(base, q=True, ws=True, rp=True))
end_vect = mapi.MVector(cmds.xform(end, q=True, ws=True, rp=True))
joint_vect = base_matrix * ((end_vect - base_vect) / (num_joints + 1.0))
mid_chain = []
for i in range(num_joints):
mid_chain.append(cmds.joint(relative=True, position=joint_vect))
cmds.parent(end, mid_chain[-1])
return mid_chain
def insert_joints(base, end, name='mid_{0:0>2d}_JNT', num_joints=1):
'''Insert unparent joints between a base joint and end joint.
:param base: Base joint to insert under
:param end: End joint to reparent under new chain
:param num_joints: Number of joints to insert (default=1)
'''
base_matrix = mapi.MMatrix(cmds.xform(base, q=True, ws=True, matrix=True))
base_vect = mapi.MVector(cmds.xform(base, q=True, ws=True, rp=True))
end_vect = mapi.MVector(cmds.xform(end, q=True, ws=True, rp=True))
joint_vect = ((end_vect - base_vect) / (num_joints + 1.0))
mid_joints = []
for i in range(num_joints):
cmds.select(clear=True)
jnt = cmds.joint(name=name.format(i))
cmds.xform(jnt, ws=True, matrix=base_matrix)
cmds.xform(jnt, ws=False, relative=True, translation=joint_vect * (i + 1))
cmds.makeIdentity(jnt, rotate=True, apply=True)
mid_joints.append(jnt)
return mid_joints
def curve(points, degree, knots=None, periodic=False):
'''Create a nurbsCurve of a certain degree, automagically figure out knots.
:param points: list of OpenMaya.MVectors or lists of x, y, z values
:param degree: Degree of curve
'''
clean_points = []
for point in points:
if isinstance(point, OpenMaya.MVector):
clean_points.append([point.x, point.y, point.z])
else:
clean_points.append(point)
if degree == 1:
return cmds.curve(p=clean_points, d=1, periodic=False)
if knots is None: # Calculate knots
knots = []
num_knots = len(clean_points) + degree - 1
for i in xrange(num_knots):
if i < degree:
knots.append(0)
elif i > num_knots - degree:
knots.append(knots[-1])
else:
knots.append(knots[-1] + 1)
return cmds.curve(p=clean_points, d=degree, k=knots, periodic=periodic)
def ribbon_surface(start_pos, end_pos, width,
divisions, up_vec=(0, 1, 0), pad=True):
'''Create a surface along the length of a joint.
:param start_pos: start of surface in world space
:param end_pos: end of surface in world space
:param joint: joint to create ribbon along
:param divisions: number of surface divisions
'''
start_pos = mapi.MVector(start_pos)
end_pos = mapi.MVector(end_pos)
vec = (end_pos - start_pos)
length = vec.length()
normal_vec = vec.normal()
up_vec = mapi.MVector(up_vec)
ortho_vec = normal_vec ^ up_vec
up_vec = normal_vec ^ ortho_vec
pnt_step = length / divisions
plus_curve_pnts = []
minus_curve_pnts = []
for x in xrange(divisions + 1):
pos = start_pos + (normal_vec * x * pnt_step)
plus_curve_pnts.append(pos + (up_vec * width * 0.5))
minus_curve_pnts.append(pos - (up_vec * width * 0.5))
degree = 3 if divisions > 1 else 1
if degree == 3 and pad:
for crv_pnts in [plus_curve_pnts, minus_curve_pnts]:
start_a, start_b = crv_pnts[:2]
end_a, end_b = crv_pnts[-2:]
crv_pnts.insert(1, start_a + (start_b - start_a) * 0.3334)
crv_pnts.insert(-1, end_a + (end_b - end_a) * 0.6667)
plus_curve = curve(points=plus_curve_pnts, degree=degree)
minus_curve = curve(points=minus_curve_pnts, degree=degree)
surface = cmds.loft(plus_curve, minus_curve, degree=1, ch=False)[0]
cmds.delete(plus_curve, minus_curve)
return surface
def ribbon_surface_from_nodes(nodes, *args, **kwargs):
start_pos, end_pos = [cmds.xform(node, q=True, ws=True, rp=True)
for node in nodes]
return ribbon_surface(start_pos, end_pos, *args, **kwargs)
def ribbon_rig(start_jnt, end_jnt, num_controls, num_joints, *args, **kwargs):
start_pos = cmds.xform(start_jnt, q=True, ws=True, rp=True)
start_mat = cmds.xform(start_jnt, q=True, ws=True, matrix=True)
end_pos = cmds.xform(end_jnt, q=True, ws=True, rp=True)
end_mat = cmds.xform(end_jnt, q=True, ws=True, matrix=True)
# Nurbs surfaces
control_surface = ribbon_surface(
start_pos,
end_pos,
width=1,
divisions=1,
up_vec=start_mat[4:7])
control_surface = cmds.rename(control_surface, 'ribbon_CTRL_SURF#')
joint_surface = ribbon_surface(
start_pos,
end_pos,
width=1,
divisions=num_joints + 1,
pad=True,
up_vec=start_mat[4:7])
joint_surface = cmds.rename(joint_surface, 'ribbon_JNT_SURF#')
# Joints
aim_joints = [
cmds.duplicate(start_jnt, name='ribbon_start_AIM_JNT#', po=True)[0],
cmds.duplicate(end_jnt, name='ribbon_end_AIM_JNT#', po=True)[0]]
ctrl_locs = []
for mat, aim_joint in zip([start_mat, end_mat], aim_joints):
cmds.select(aim_joint, replace=True)
cmds.joint(
name=aim_joint.replace('AIM', 'AIM_END'),
relative=True,
position=[1, 0, 0])
offset = mapi.MVector(mat[8:11]) + mapi.MVector(mat[12:15])
up_mat = mat[:12] + list(offset) + mat[-1:]
pos_loc = cmds.spaceLocator(name=aim_joint.replace('_AIM_JNT', ''))[0]
up_loc = cmds.spaceLocator(name=aim_joint.replace('AIM_JNT', 'up'))[0]
cmds.xform(pos_loc, ws=True, matrix=mat)
cmds.xform(up_loc, ws=True, matrix=up_mat)
cmds.parent((up_loc, aim_joint), pos_loc)
ctrl_locs.append((pos_loc, up_loc))
cmds.aimConstraint(
ctrl_locs[1][0],
aim_joints[0],
worldUpType='objectrotation',
worldUpObject=ctrl_locs[0][1])
cmds.aimConstraint(
ctrl_locs[0][0],
aim_joints[1],
worldUpType='objectrotation',
worldUpObject=ctrl_locs[1][1])
ctrl_joints = insert_joints(
start_jnt,
end_jnt,
name='j_{0:0>2d}_j#',
num_joints=num_controls)
ctrl_joints.insert(0, cmds.duplicate(start_jnt, po=True)[0])
ctrl_joints.append(cmds.duplicate(end_jnt, po=True)[0])
cmds.hide(ctrl_joints)
cmds.parent([ctrl_joints[0], ctrl_joints[-1]], world=True)
for i, j in enumerate(ctrl_joints):
ctrl_joints[i] = cmds.rename(j, 'ribbon_{0:0>2d}_JNT#'.format(i + 1))
bind_joints = insert_joints(
start_jnt,
end_jnt,
name='ribbon_{0:0>2d}_BIND_JNT#',
num_joints=num_joints)
ctrl_groups = []
for ctrl in ctrl_joints:
ctrl_group, ctrl_curve = add_control(
ctrl,
name=ctrl.replace('JNT', 'CTRL'),
typ='circle',
color='yellow',
axis=(1, 0, 0),
scale=cmds.getAttr(start_jnt + '.radius') * 0.667,
constrain=False)
ctrl_groups.append(ctrl_group.split('|')[-1])
ctrl_rivets = multi_rivet(ctrl_groups, control_surface, constrain=True)
bind_rivets = multi_rivet(bind_joints, joint_surface, constrain=True)
rivet_grp = cmds.group(ctrl_rivets + bind_rivets, name='ribbon_RIV_GRP#')
bind_grp = cmds.group(bind_joints, name='ribbon_BIND_GRP#')
cmds.skinCluster(aim_joints, control_surface, toSelectedBones=True)
cmds.skinCluster(ctrl_joints, joint_surface)
return ctrl_locs, bind_grp, rivet_grp, ctrl_groups, (control_surface, joint_surface)
def override_color(color=None, *nodes):
"""Sets override color for transform or shape.
:param color: The name of a color or an int(0-31)"""
colors = {
"blue": 6,
"red": 13,
"yellow": 17,
"light yellow": 22,
"green": 26,
"light green": 27,
"light blue": 29,
}
if color in colors:
color = colors[color]
for node in nodes:
cmds.setAttr(node + ".overrideEnabled", 1)
cmds.setAttr(node + ".overrideColor", color)
def add_control(node, name, axis=(0, 1, 0), scale=1, typ='circle', color=None,
constrain=False):
'''Add a control to a node.
:param node: Node that needs a new control
:param name: Name of the new control
:param typ: Type of control to create
:param color: Color of control to create
:param constrain: Use a parent constraint instead of direct parenting
'''
per, deg, pnts, knts = CURVE_CTRLS[typ]
ctrl = curve(points=pnts, degree=deg, knots=knts, periodic=per)
ctrl = cmds.rename(ctrl, name)
ctrl_shape = get_shape(ctrl)
scale = [scale for i in xrange(3)]
if color is not None:
override_color(color, ctrl_shape)
if axis == (1, 0, 0):
cmds.xform(ctrl, ws=True, scale=scale, rotation=mapi.MVector((0, 0, 1)) * 90)
elif axis == (0, 0, 1):
cmds.xform(ctrl, ws=True, scale=scale, rotation=mapi.MVector((1, 0, 0)) * 90)
cmds.makeIdentity(apply=True, rotate=True)
if not constrain:
ctrl_grp = insert_parent(node)
cmds.parent(ctrl, ctrl_grp, relative=True)
cmds.parent(node, ctrl)
else:
ctrl_grp = cmds.group(ctrl)
cmds.xform(
ctrl_grp,
ws=True,
matrix=cmds.xform(node, q=True, ws=True, matrix=True))
cmds.parentConstraint(ctrl, node)
return ctrl_grp, ctrl
def ribbon_limb(joints, num_controls, num_joints):
'''Add a ribbon between each set of joints in a chain.
:param joints: list of joints to add ribbons to
'''
old_selection = cmds.ls(sl=True, long=True)
cmds.select(clear=True)
start_jnt, mid_jnt, end_jnt = joints
# Setup mid section
mid_radius = cmds.getAttr(mid_jnt + '.radius')
mid_control = cmds.circle(
name='mid_LOC#',
radius=mid_radius * 2.5,
normal=(1, 0, 0))[0]
mid_bind_jnt = cmds.joint(name='mid_BIND_JNT#', radius=mid_radius * 1.5)
cmds.parent(mid_control, mid_jnt, relative=True)
cmds.parentConstraint(mid_control, mid_bind_jnt)
mid_grp = insert_parent(mid_control)
cmds.orientConstraint(start_jnt, mid_jnt, mid_grp)
cmds.select(clear=True)
# Setup Ribbons
ribbon_a = ribbon_rig(start_jnt, mid_jnt, num_controls, num_joints)
ribbon_b = ribbon_rig(mid_jnt, end_jnt, num_controls, num_joints)
a_ctrls, a_bind, a_rivet, a_ctrl_groups, a_surfaces = ribbon_a
b_ctrls, b_bind, b_rivet, b_ctrl_groups, b_surfaces = ribbon_b
cmds.pointConstraint(start_jnt, a_ctrls[0][0])
cmds.pointConstraint(mid_control, a_ctrls[1][0])
cmds.orientConstraint(mid_control, a_ctrls[1][0], skip='y')
cmds.orientConstraint(mid_control, b_ctrls[0][0], skip='y')
cmds.pointConstraint(mid_control, b_ctrls[0][0])
cmds.pointConstraint(end_jnt, b_ctrls[1][0])
cmds.orientConstraint(end_jnt, b_ctrls[1][0], skip='none')
# Regroup it all
bind_joints = [mid_bind_jnt]
bind_joints.extend(cmds.listRelatives(a_bind, children=True))
bind_joints.extend(cmds.listRelatives(b_bind, children=True))
bind_grp = cmds.group(bind_joints, name='ribbon_BIND#')
ctrl_groups = a_ctrl_groups + b_ctrl_groups
ctrl_grp = cmds.group(ctrl_groups, name='ribbon_CTRLS#')
scale_grp = cmds.group(empty=True, name='ribbon_SCALE_GRP#')
cmds.delete(cmds.parentConstraint(start_jnt, scale_grp))
cmds.parent([bind_grp, ctrl_grp], scale_grp)
cmds.parent([a_ctrls[0][0], a_ctrls[1][0]], scale_grp)
cmds.parent([b_ctrls[0][0], b_ctrls[1][0]], scale_grp)
extras = cmds.group(name='ribbon_EXTRAS#', empty=True)
cmds.parent(a_surfaces, extras)
cmds.parent(b_surfaces, extras)
cmds.parent([a_rivet, b_rivet], extras)
cmds.hide([a_ctrls[0][0], a_ctrls[1][0], b_ctrls[0][0], b_ctrls[1][0]])
cmds.hide(extras)
cmds.hide(bind_grp)
#Clean up
cmds.delete(a_bind, b_bind)
cmds.select(old_selection)
return
if __name__ == '__main__':
cmds.undoInfo(openChunk=True)
try:
ribbon_limb(cmds.ls(sl=True, type='joint'), 2, 5)
except Exception as e:
traceback.print_exc()
finally:
# clean_up()
cmds.undoInfo(closeChunk=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment