[Maya Rigging] :: Blend-Shape Based Face Rig.

Today I will explain how a blend-shape based face rig works for autodesk Maya. Understand that blendShapes is an additive mesh shape deformer, that one after another shape gets activated and can be driven by a single controller with values from 0.0 to 1.0 to drive shapes: shape0 + shape0_5 + shape1_0.

I had trouble finding a face mesh to work with, so I headed over to AnimSchool and downloaded their Malcom Rig and extracted the head mesh for me to work on:

https://www.animschool.com/DownloadOffer.aspx

While the set-up of the controllers are rather simple, each controller had to have a maximum value of 1. In addition to that, here is work needs to be done in creating the shapes themselves. For this reason, I chose to play with OpenMaya::MFnBlendShape class to add, and remove shape targets.

To initialize a blend-shape node without targets (important step):

def create_blendshape(mesh_objects, name=""):
    """
    creates a new blendShape from the array of mesh objects provided
    :param mesh_objects: <tuple> array of mesh shapes.
    :param name: <str> name of the blendshape.
    :return: <OpenMayaAnim.MFnBlendShapeDeformer>
    """
    blend_fn = OpenMayaAnim.MFnBlendShapeDeformer()

    if isinstance(mesh_objects, (str, unicode)):
        mesh_obj = object_utils.get_m_obj(mesh_objects)
        blend_fn.create(mesh_obj, origin, normal_chain)

    elif len(mesh_objects) > 1 and isinstance(mesh_objects, (tuple, list)):
        mesh_obj_array = object_utils.get_m_obj_array(mesh_objects)
        blend_fn.create(mesh_obj_array, origin, normal_chain)
    else:
        raise ValueError("Could not create blendshape.")

    if name:
        object_utils.rename_node(blend_fn.object(), name)
    return blend_fn

Each blend-shape index starts from 5000 and ends at 6000, so to get indices we need to use OpenMaya.MIntArray(), please understand that we need to use MIntArray and not a list of integers because otherwise Maya will not accept those integers:

def get_weight_indices(blend_name=""):
    """
    get the weight indices from the blendShape name provided.
    :param blend_name: <str> the name of the blendShape node.
    :return: <OpenMaya.MIntArray>
    """
    blend_fn = get_deformer_fn(blend_name)
    int_array = OpenMaya.MIntArray()
    blend_fn.weightIndexList(int_array)
    return int_array

Now we can add shape targets like by using the following code, the objects are accepted from targets_array and Maya’s specified index:

def add_target(targets_array, blend_name="", weight=1.0, index=0):
    """
    adds a new target with the weight to this blend shape.
    Maya has a fail-safe to get the inputTargetItem from 6000-5000
    :param targets_array: <tuple> array of mesh shapes designated as targets.
    :param blend_name: <str> the blendShape node to add targets to.
    :param weight: <float> append this weight value to the target.
    :param index: <int> specify the index in which to add a target to the blend node.
    :return:
    """
    blend_fn = get_deformer_fn(blend_name)
    base_obj = get_base_object(blend_name)[0]
    if isinstance(targets_array, (str, unicode)):
        targets_array = targets_array,
    targets_array = object_utils.get_m_shape_obj_array(targets_array)
    length = targets_array.length()
    if not index:
        index = get_weight_indices(blend_fn.name()).length() + 1
    # step = 1.0 / length - 1
    for i in xrange(0, length):
        # weight_idx = (i * step) * 1000/1000.0
        blend_fn.addTarget(base_obj, index, targets_array[i], weight)
    return True

One after another we an add all the shapes by code in whatever order of targets we want, adding in-betweens from 0.0 -> 1.0. I always choose to go in steps of “5”-ves. (0.0, 0.25, 0.5, 0.75, 1.0). This is because don’t really need to go any more complicated that that.

Over the course of constructing the blend-shape based rig, you need to have two base meshes: one for deformation and the other for duplicating mesh objects for sculpting. I used abSymMesh for mesh mirroring because the tool is already there and I did not need to re-invent another one. All in all I think I’ve done a good job with my face:

Face rig video of my repurposes Malcom face rig.

The complete module I used in this construction can be found at my GitHub page:

Leave a Reply

Your email address will not be published. Required fields are marked *