Website Youtube GitHub

Eyebrow rigging


Can anyone help to break down how you would build eyebrows with mgear.
Maybe one of the guide components can be used?

Would really appreciate if someone has already tried this and been successful.

Also if someone has managed to add cheek joints that are connected to the jaw open and close?



Hi Benn, this is how I do my brows:

  • I make some Control_01 modules for brow controls. 3 on each side, and one in the middle.
  • I use mgear.core.curve.addCnsCurve() to add a curve that is connected to those controls.
  • Then you can either use motion path constraints, like the lips, or I personally turn the curve into a lofted nurbs ribbon, add joints to the ribbon, and skin that to my head.

As for connecting cheeks to the jaw opening, what did you try? What problems are you having? You can use whatever you want. A remapValue node. Driven keys.


A couple other points about what I do:

When you use mgear.core.curve.addCnsCurve(), you can specify a control twice, and it makes a nice sharp tangent in your curve. Which is helpful for the corners of eyebrows:

Also, I have 2 more Control_01 controls to be the parent of the 3 right controls and the 3 left controls. Which makes it easier to move the entire brow. And the middle control is pointConstrained to both sides, so it follows in the middle.



This is an amazing response Chris!!
How do I use the mgear.core.curve.addCnsCurve()



Here’s how my brows look at the moment.


Docs are here:

from mgear.core import curve
import pymel.core as pm

# The node to parent your curve underneath
myParent = pm.PyNode('brow_grp')
# A list of PyNodes for the controls that will connect the curve
# And I double up 2 and 4, to get a sharp tangent
browControls = [brow0, brow1, brow2, brow2, brow3, brow4, brow4, brow5, brow6]
# 3 means it will be a cubic curve. 1 would be linear.
browCurve = curve.addCnsCurve(myParent, 'name_of_brow_curve', browControls, 3)


Thanks a lot Chris.
As for the cheek control I haven’t tried setting a sdk.
Could this be scripted also?



Setting a dk. You set a driven key. :wink:

Yeah that could be scripted. Consider using a remapValue node instead. It’s a node that takes an input (Your jaw rotation) and a min and max input range, and spits out a min and max output range. It is great for driving a simple relationship like this.


So much good info here but the scripting terminology is like greek to me lol


Could you explain the way motion path constraints work and how you would setup this up for the brows?
When it comes to using a ribbon setup. You create the ribbon independent from mgears setup right?
Then parent the new joints to the head bone/joint.
And use the joints created by mgear( the three for the left and three for the right) to control the ribbon joints?



Chris do you think you can do a video or image process of this? I can’t understand scripting.


Hi @chrislesage

Can you please explain how you convert your brow curve into a ribbon and still have the brow curve affecting your ribbon. That would be really helpful.



Hi @niteshms04

My curves do not affect the ribbon. I make two curves, and them. Then I put follicles on the lofted nurbsSurface, and the ribbon is just skinned to my brow controls. The original curves get deleted, and the loft has no construction history.

This is my guide. A bunch of simple control_01 nodes. The bigger ones are just parents so you can move 3 at a time.

This is not complete code, but maybe you can see a bit what I am doing:

# Get a list of objects to define and constraint the CnsCurve
# CnsCurve in mGear follows the objects.
# double up points on [3] and [5] for a sharper brow edge. This is what I showed in an image further up, where you get a nice sharp corner.
fancyOrder = [0,1,2,3,3,4,5,5,6,7,8]
# These are the lists of my brow controls.
browCurvePos = [oBrowControls[i] for i in fancyOrder]
# The upvector controls are just pushed somewhat forward in translateZ
browCurveUpPos = [oBrowUps[i] for i in fancyOrder]
browJoints2 = [browJoints[i] for i in fancyOrder]

browCurve = curve.addCnsCurve(browComponents, 'brow_curve', browCurvePos, 3)
browUpCurve = curve.addCnsCurve(browComponents, 'brow_upv_curve', browCurveUpPos, 3)
# generate a ribbon from the CnsCurves. mGear likes motion paths, and I say BLEGH!!!!!
oLoft =, browUpCurve, ch=False, range=True, n='brow_ribbon')[0]
# Skin the loft (my own skin function, but nothing fancy.)
oSkin = tenave.skin_geometry(browJoints, oLoft, 'brow_ribbon_skinCluster')
# Set the skinning 1 row per each joint.
# Keeping in mind that browCurvePos is a list where the controls repeat themselves for hard tangent purposes.
for ii, each in enumerate(browJoints2):
    cvRow =[0:][ii]
    pm.skinPercent( oSkin, cvRow, transformValue=[(each, 1)])
pm.delete(browCurve, browUpCurve)

Rope as a Shifter Component?

Thanks @chrislesage. That makes sense.