(tested in Blender 2.79, there may be bugs so please modify use at your own risk)
"""methods to be used for joint driver scripted expressions.
they would need to be added to bpy driver namespace
"""
"""
tested in blender 2.79
please modify use at your own risk
"""
import bpy
def addExprToDriver():
"""add expressions to driver name space
"""
#between expression
exprName = "expr_between"
#replace expression if it exists
bpy.app.driver_namespace[exprName] = eval(exprName)
#blink blendshape expression
exprName = "expr_blink"
bpy.app.driver_namespace[exprName] = eval(exprName)
#lip corner blendshape expression
exprName = "expr_lipCorner"
bpy.app.driver_namespace[exprName] = eval(exprName)
#add additional driver methods here
#add wrapper functions here - to be called in driver expression
def expr_between(value, maxValue=1, minValue=-1):
"""see class for parameter description
"""
driverObj = LimitDriver()
return driverObj.between(value, maxValue=maxValue, minValue=minValue)
def expr_blink(value, inbetween="halfclose"):
"""see class for parameter description
"""
driverObj = BlinkBlendshapeDriver()
return driverObj.blink(value, inbetween=inbetween)
def expr_lipCorner(valueX, valueY, inbetween="out"):
"""see class for parameter description
"""
driverObj = LipCornerBlendshapeDriver()
return driverObj.lipCorner(valueX, valueY, inbetween=inbetween)
#add additional wrapper functions here
class LimitDriver(object):
"""drivers related to limiting movement
"""
def between(self, value, maxValue=1, minValue=-1):
"""when used on a joint limit its movement to be between min and max
example for brow.
example for lip corner/2d pupil could use two of these one for each axis for joint
@param value (double) an unrestricted value for joint
@param maxValue (double) maximum local value allowed
@param minValue (double) minumum local value allowed
"""
if value > maxValue:
return maxValue
if value < minValue:
return minValue
return value
class BlinkBlendshapeDriver(object):
"""half way and full shape blink driver
todo: support multiple inbetween shapes
"""
def blink(self, value, inbetween="halfclose"):
"""get weights to use to drive blink blendshape. get different weight depending on which inbetween picked
@param value (double) value for driver slider that turns on blink blendshapes
@param inbetween (str) which inbetween we need the driver value supported ("halfclose","fullclose")
"""
#currently half shape is on at 0.5 and full shape is 0. and full shape is 1 at 1.0
halfDriverValue=0.0
fullDriverValue=0.0
if value <= 0.5 and value >= 0:
#for half mapping [0,0.5] to [0,1]
halfDriverValue = value*2.0 #at 0.5 half is 1 at 0 half is 0
fullDriverValue = 0.0
if value > 0.5:
#turn off half
#map (0.5,1) to (1,0)
halfDriverValue = self._mapInterval(value, 0.5, 1.0, 1.0, 0.0)
#turn on full
#map (0.5,1) to (0,1)
fullDriverValue = self._mapInterval(value, 0.5, 1.0, 0.0, 1.0)
if value >= 1.0:
halfDriverValue = 0.0
fullDriverValue = 1.0
#possibly could edit to support more inbetweens
if inbetween == "halfclose":
return halfDriverValue
else:
return fullDriverValue
def _mapInterval(self, t, a, b, c, d):
"""map interval [a,b] to [c,d]
"""
return c + ((d-c)/(b-a))*(t-a) #can check end points. when t=a get c. when t=b get d
class LipCornerBlendshapeDriver(object):
"""out, in, up, dn, up out, up in, dn out, dn in blendshape driver
even if not all slots are used should have the capability
"""
def lipCorner(self, valueX, valueY, inbetween="out"):
"""get weights for driving lip corner blendshapes
@param valueX (double) input x value
@param valueY (double) input y value
@param inbetween (str) queried inbetween supported ("out","in","up","dn","upOut","dnOut","upIn","dnIn")
"""
outDriverValue = 0.0
inDriverValue = 0.0
upDriverValue = 0.0
dnDriverValue = 0.0
upOutDriverValue = 0.0
dnOutDriverValue = 0.0
upInDriverValue = 0.0
dnInDriverValue = 0.0
#out, in
if valueX > 0.0:
outDriverValue = valueX
if valueX < 0.0:
inDriverValue = -1*valueX
#up, dn
if valueY > 0.0:
upDriverValue = valueY
if valueY < 0.0:
dnDriverValue = -1*valueY
#up out, dn out
if valueX > 0.0 and valueY > 0.0:
upOutDriverValue = valueX*valueY
if valueX > 0.0 and valueY < 0.0:
dnOutDriverValue = valueX*(-1*valueY)
#up in, dn in
if valueX < 0.0 and valueY > 0.0:
upInDriverValue = -1*valueX*valueY
if valueX < 0.0 and valueY < 0.0:
dnInDriverValue = (-1*valueX)*(-1*valueY)
if inbetween == "out":
return outDriverValue
if inbetween == "in":
return inDriverValue
if inbetween == "up":
return upDriverValue
if inbetween == "dn":
return dnDriverValue
if inbetween == "upOut":
return upOutDriverValue
if inbetween == "dnOut":
return dnOutDriverValue
if inbetween == "upIn":
return upInDriverValue
if inbetween == "dnIn":
return dnInDriverValue
"""
import bpy
import imp
testTool = imp.load_source("tool","/Users/Nathaniel/Documents/src_blender/python/riggingTools/faceTools/joint_driver.py") #change to path to python file
testTool.addExprToDriver()
"""
#inspired by
#https://math.stackexchange.com/questions/914823/shift-numbers-into-a-different-range
#https://docs.blender.org/manual/en/latest/animation/drivers/workflow_examples.htmlHappy Scripting! and Happy Sketching!