2
$\begingroup$

I've been working with an audio-driven animation and discovered that you cannot apply a driver directly to an input field on a material node. Rather you need to navigate through the Datablock to the object and drill down until you find the node tree with the target value. Right-Click on the row (not the input field) and hit D to add a driver. The input field does not turn purple but the driver will show up in the Graphs window under drivers.

That method works but is so tedious so why not write a python script right?

My script works, it can find the active material, the node, the value and create a driver and the driver shows up but turns purple so that method is creating the driver on the material.

In the Graph window the proper driver shows up under the Object. The incorrect one shows up under the Material.

Hovering over the input field shows the correct path:

bpy.data.node_groups["Shader Nodetree"].nodes["Value"].outputs[0].default_value

If you drill into the Data-blocks and find the target node, change the value the console outputs:

bpy.data.node_groups["Shader Nodetree"].nodes["Value"].outputs[0].default_value = 1

If you add the Driver by selecting the row and hitting D it outputs:

bpy.ops.outliner.drivers_add_selected()

Which isn't very helpful as that is based on the selected row in the active context. For my tool, I just want to click on the node making it active and obviously this method is parsing the node for the material outputs and binds the driver to the material not the object.

for n in context.active_node.outputs:
            if( n.name == "Value" ):
                newdriver = n.driver_add("default_value")
                var = newdriver.driver.variables.new()
                var.name='audDrv'
                var.type='SINGLE_PROP'
                var.targets[0].id_type = 'WORLD'
                var.targets[0].id = bpy.data.worlds["World"]
                var.targets[0].data_path = '["TestCurve"]'
                newdriver.driver.expression = var.name

Rather, when I click on the node and apply the driver it needs to reference the object the material is active on. Find the animation data, the driver and bind the node path.

So back the drawing board. I was able to reproduce the Path that Blender shows when you hover over the input field of the targeted node value in the Data-Block tree.

# active_mat_name = bpy.context.object.active_material.name
        active_node_tree = bpy.context.object.active_material.node_tree.name 
        active_node_name = context.active_node.name
        print( "Active Material: %s" % bpy.context.object.active_material.name )
        print( "Active Node Tree: %s" % active_node_tree )                 
        print( "Active Node: %s" % context.active_node.name )

        # This may work using format to produce the datapath string required.
        # Ref: https://docs.python.org/3/library/string.html#format-string-syntaxt
        dpath = "bpy.context.scene.node_groups[\"{0}\"].nodes[\"{1}\"].outputs[0].default_value".format(active_node_tree,active_node_name)
        bpy.context.object.active_material.node_tree.nodes[active_node_name].outputs[0].default_value

        print( "Path: %s" % dpath)

        # Now for the Active Object
        object = bpy.context.active_object
        print( "Active Object: %s" % object )                  

        newdriver = object.driver_add('location',1)
        #newdriver = bpy.context.object.active_material.node_tree.nodes[active_node_name].outputs[0].default_value
        newdriver.data_path = bpy.context.object.active_material.node_tree.nodes[active_node_name].outputs[0].default_value
        # create a variable
        var = newdriver.driver.variables.new()
        var.name='audDrv'
        # set the type first (default is 'SINGLE_PROP')
        var.type='SINGLE_PROP' # you can use 'SINGLE_PROP', 'LOC_DIFF' or 'ROTATION_DIFF'
        var.targets[0].id_type = 'WORLD'
        var.targets[0].id = bpy.data.worlds["World"]
        var.targets[0].data_path = '["TargetProp"]'
        # now set the expression on the driver            
        newdriver.driver.expression = var.name

Now the drive appears under the cube but still doesn't work changing the material value. I've tried many methods but the end result are all end up either putting the driver on the material turning the field purple or simply not working at all.

It's very ambiguous as to what specifically the difference is between adding a driver directly to the material node or adding it to the node in the data-block tree again on the row selected specifically and not the value field.

Thought I had it figured out but now I need to turn to hopefully someone here who might have already gone through this process.

$\endgroup$
  • $\begingroup$ I don't understand. You can add a driver to a material input field. $\endgroup$ – cmomoney Nov 10 '17 at 22:24
  • $\begingroup$ You can, but it doesn't update from the F-Curve that is being used to drive the animation in 2.79. It has to be bound in the data-block tree and not on the input rather the row the input is on. Do that, it works great. Now how to program for that because navigating the tree to find the node is very tedious. $\endgroup$ – Ian Wilson Nov 10 '17 at 22:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.