Saturday, May 25, 2019

Blender Script to Create face rig corrective shapes

Hi,
This is a simple script i wrote to help create facial rig corrective shapes in Blender.
Its still a work in progress so use/modify at your own risk.
It works by giving it two sculpted blendshapes and a combo sculpted blendshape.
The script produces a corrective shape that when added to the two shapes produces the combo shape.

Some example images:

first sculpt two shapes. Example corners wide, and a corner up shape.
As can see when both are on the look needs improvement.
so hand sculpt a combo shape.
But the issue is this can not be directly added on top of the two single shapes.
This is where the script comes in.
running script produces a corrective shape that can be added on top of the two single shapes to produce the look of the combo. Here created corrective is called 'corrective_upBack'

#naBlendshapeCombo.py

#use modify at own risk

import bpy


def run(firstShape = None, secondShape = None, comboShape = None):
    """create corrective shape from two blendshapes and a sculpted combo shape.
    assuming all shapes already added as shape keys
    first+second+x = combo
    x = combo - (first + second)
    this should give x shape -- corrective result
    so first 1, second 1 and corrective 1 = comboShape
    """
    #firstShape = 'back'
    #secondShape = 'up'
    #comboShape = 'upBack'
    baseShape = 'Basis' #assuming base shape is Basis blenders default change if you change base shape name
    
    #first+second+x = combo
    #x = combo - (first + second)
    #this should give x shape -- corrective result
    obj = bpy.context.active_object
    firstShapeIndex = obj.data.shape_keys.key_blocks.keys().index(firstShape)
    secondShapeIndex = obj.data.shape_keys.key_blocks.keys().index(secondShape)
    baseShapeIndex = obj.data.shape_keys.key_blocks.keys().index(baseShape)
    comboShapeIndex = obj.data.shape_keys.key_blocks.keys().index(comboShape)
    
    verts = obj.data.vertices
    correctiveVertDict = {} #will hold tuple (x,y,z) for each vertex of corrective shape
    for i in range( len(verts) ):
        #so can find deltas
        baseX = obj.data.shape_keys.key_blocks[baseShapeIndex].data[i].co.x
        baseY = obj.data.shape_keys.key_blocks[baseShapeIndex].data[i].co.y
        baseZ = obj.data.shape_keys.key_blocks[baseShapeIndex].data[i].co.z
        
        
        correctiveX = (baseX-obj.data.shape_keys.key_blocks[comboShapeIndex].data[i].co.x) -\
        ( (baseX - obj.data.shape_keys.key_blocks[firstShapeIndex].data[i].co.x) + (baseX - obj.data.shape_keys.key_blocks[secondShapeIndex].data[i].co.x) )
        correctiveY = (baseY-obj.data.shape_keys.key_blocks[comboShapeIndex].data[i].co.y) -\
        ( (baseY - obj.data.shape_keys.key_blocks[firstShapeIndex].data[i].co.y) + (baseY - obj.data.shape_keys.key_blocks[secondShapeIndex].data[i].co.y) )
        correctiveZ = (baseZ-obj.data.shape_keys.key_blocks[comboShapeIndex].data[i].co.z) -\
        ( (baseZ - obj.data.shape_keys.key_blocks[firstShapeIndex].data[i].co.z) + (baseZ - obj.data.shape_keys.key_blocks[secondShapeIndex].data[i].co.z) )


        correctiveVertDict[i] = (correctiveX,correctiveY,correctiveZ)
        
    #add new corrective shape. so first 1, second 1 and corrective 1 = comboShape
    correctiveShape = 'corrective_%s' %comboShape
    corrective = obj.shape_key_add(correctiveShape)
    corrective.interpolation = 'KEY_LINEAR'
    #position vertices
    for i in range(len(verts)):
        pos = correctiveVertDict[i]
        corrective.data[i].co.x -= pos[0]
        corrective.data[i].co.y -= pos[1]
        corrective.data[i].co.z -= pos[2]

"""
from importlib import reload
import sys
sys.path.append('/Users/Nathaniel/Documents/src_blender/python/snippets/blendshapes/')
import naBlendshapeCombo as mod
reload(mod)

mod.run('up','back','upBack')
"""


#inspired by:
#Eyad Hussein's https://eyad.tv/home/advanced-rigging/ tips on combo shapes
#https://blender.stackexchange.com/questions/111661/creating-shape-keys-using-python
# 
 
Happy Sketching!
Nate