How do I add drivers to materials and textures?

I’m trying to create drivers for some material and texture settings. A typical application is facial expressions, where wrinkles are made with a bump map. However, drivers for materials and textures evidently work differently from ordinary drivers. But how?

The following program creates drivers for three properties of the default cube: an object setting (visibility), a material setting (use_textures[0]), and a texture setting (noise depth) are all driven by object properties of an armature.

import bpy

def run():
    bpy.ops.object.armature_add()
    bpy.ops.transform.translate(value=(0, -2, 0))
    rig = bpy.context.object
    rig["Hide"] = False
    rig["Tex"] = True
    rig["Depth"] = 4

    tex = bpy.data.textures.new("Tex", type = "CLOUDS")
    mat = bpy.data.materials.new("Mat")
    mtex = mat.texture_slots.add()
    mtex.texture = tex
    cube = bpy.data.objects["Cube"]
    cube.data.materials[0] = mat
    
    fcu = cube.driver_add("hide", -1)
    setDriver(fcu, rig, '["Hide"]')
    
    fcu = cube.material_slots[mat.name].material.driver_add("use_textures", 0)    
    #fcu = cube.driver_add("material_slots[\"%s\"].material.use_textures" % mat.name, 0)
    setDriver(fcu, rig, '["Tex"]')

    fcu = cube.material_slots[mat.name].material.texture_slots[tex.name].texture.driver_add("noise_depth", -1)
    #fcu = cube.driver_add("material_slots[\"%s\"].material.texture_slots[\"%s\"].texture.noise_depth" % (mat.name, tex.name), -1)
    setDriver(fcu, rig, '["Depth"]')
    return
    
def setDriver(fcu, rig, datapath):    
    drv = fcu.driver
    drv.type = "SCRIPTED"
    drv.expression = "x"
    drv.show_debug_info = True

    var = drv.variables.new()
    var.name = "x"
    var.type = "SINGLE_PROP"

    targ = var.targets[0]
    targ.id = rig
    targ.data_path = datapath
    return

run()

The object setting works fine; the object is hidden when the armature property is set. However, the material and texture drivers are dead. The drivers are there (cf picture) and the corresponding settings are purple, but the driver values do not react when I change the armature properties.


However, I can add working drivers in the viewport. Weirdly enough, the working drivers turn out to belong to the object itself, and not to the material and textures. Also I note that the driven settings do not turn purple. Anyway, the driven data_path can be read off from the picture below.


However, if I change the program accordingly (comment/uncomment the driver_add lines), the following error appears:

Traceback (most recent call last):
  File "\matdriver.py", line 45, in <module>
  File "\matdriver.py", line 22, in run
ValueError: bpy_struct.driver_add(): path spans ID blocks

Does anyone have an idea how to get around this. The logged operator when I add working drivers is bpy.ops.anim.driver_button_add(all=False), but that is of little use to me since I don’t know how to select a button from python.

Hi ThomasL,

Hope you find an answer to this. Come across this myself with the Evaluation Time on paths.
Couple of things while i was fiddling, had to replace


cube.data.materials[0] = mat

with


cube.data.materials.append(mat)

to get it to work out of the box… and also found that


(x>0) 

works as a driver expression.

You can work around it by using a driver function and passing it the paths/variables to the materials/textures and setting them there.

Very simplistic example.


import bpy
def mydriver(x,id,mat,index,value):
    obj = bpy.data.objects[id]
    mat = obj.material_slots[mat].material
    mat.use_textures[index] = value
    return(x>0)

bpy.app.driver_namespace["mydriver"] = mydriver
    
#mydriver(x,"Cube","Mat",0,value>0)

Replaced your visibility driver with this. value is the driver variable for Armature[“Tex”]

Not ideal but…

Hi ThomasL,

I’ve just spent a few hours trying to solve the same problem. Found a clue in your bug report here (http://projects.blender.org/tracker/?func=detail&atid=498&aid=28968&group_id=9).

Creating a dummy driver on the object then redirecting the data_path of the f-curve that is returned appears to work.

So given:
‘Arrows-Built’ is an object in my scene.
‘Arrows-Built-A2’ is one of the materials assigned to the object.

At the interactive console:
>>> oGeometry = bpy.data.objects[‘Arrows-Built’]
>>> fc = oGeometry.driver_add(‘hide’)
>>> fc.data_path = ‘material_slots[“Arrows-Built-A2”].material.diffuse_color’
etc to setup driver expressions.

I create a driver for the hide property only to get the driver attached to the object as occurs when you create the driver from the UI. Changing the datapath redirects it to the material colour that I really wanted to change.

HTH
paul.

Hi Paul,
I hope this message will arrive to you.

Does your code create a driver option on the eye icon?
This option used to exist on Blender 2.79 and disappeared since 2.8+.
(I waste so much time failing to find a way to reproduce what I could do with this option.)