12
$\begingroup$

I'm trying to write a script to create the beginnings of a music visualizer but I'm having a hard time creating drivers to do it. My basic game plan is this:

  1. Calculate frequencies and step sizes
  2. For each frequency step, create a cube.
  3. Create an empty to go along with it.
  4. Do some transformations on the cube.
  5. Add a driver to the cube.
  6. Link the cube's Z scale with the empty's scale using drivers.
  7. Create a material that also uses drivers to hook into the empty's scale and use that to animate stuff.
  8. Bake sound to empty's scale.
  9. Rinse and repeat until all cubes/empties/materials are created.

The reason I want to use drivers is that my previous approach was to bake the sound to the cube's Z scale then I had another script that would read that information for each frame and then keyframe the cube's material. This led to pretty big file sizes. Larger than it needed to be, anyway. So this time around I figured I'd get around that by using drivers. I've been playing around in the console trying to figure it out and this is what I've got so far:

bpy.ops.mesh.primitive_cube_add(location=(.5, -.5, 0)) # Create a cube
currentcube = bpy.context.active_object # Make sure we can keep track of this cube while we're working on it.
currentcube.name="cube.001"
bpy.ops.object.empty_add(location=(.5, -.5, -1)) # Add our control-freak empty.
currentempty=bpy.context.active_object # Make sure we can keep track of this empty while we're working on it.
currentempty.name="empty.001"
currentcube.dimensions = (1, 1, 5) # Do some transformations.
bpy.ops.object.transform_apply(scale=True) # Apply the transformations.
fcurve = currentcube.driver_add("scale", 2) # Add a driver to the scaling on the Z axis.
fcurve.driver.type="AVERAGE" # Default is SCRIPTED for some reason.
drivervar = fcurve.driver.variables.new() # Create a new variable on the driver.

And it's at this point that I'm stuck. dir() and autocomplete got me this far but now it's failed me and I'm having a hard time working my way through the docs. I know drivervar is a DriverVariable. From the docs it looks like I need to add a new DriverTarget into targets, but it's read-only.

But that's okay because while organizing my thoughts and the random code bits I had been trying in the console I found out that there's already a blank target by default at drivervar.targets[0] and it's not just an empty array like I had assumed. I would like to thank everyone here for the wonderful help and expertise I'm sure you would have provided if I hadn't discovered this on my own.

$\endgroup$

1 Answer 1

30
$\begingroup$

This is a general function I use in many of my scripts for adding drivers. You're welcome to implement it in your own script. It's pretty general and supports inverse relationships between properties (if one property goes up, the driven property goes down by the same amount), and also adding functions to the scripted expression (such as sin, cos, max, round, radians, etc).

def add_driver(
        source, target, prop, dataPath,
        index = -1, negative = False, func = ''
    ):
    ''' Add driver to source prop (at index), driven by target dataPath '''

    if index != -1:
        d = source.driver_add( prop, index ).driver
    else:
        d = source.driver_add( prop ).driver

    v = d.variables.new()
    v.name                 = prop
    v.targets[0].id        = target
    v.targets[0].data_path = dataPath

    d.expression = func + "(" + v.name + ")" if func else v.name
    d.expression = d.expression if not negative else "-1 * " + d.expression

You can then use it to link you empty and cube's Z scale this way for instance:

cube  = bpy.context.scene.objects['Cube']
empty = bpy.context.scene.objects['Empty']
add_driver( cube, empty, 'scale', 'scale.z', 2 )

I do have to say, though, that if you want to link the empty and the cube's scale, all you need to do is parent the cube to the empty, no driver is required.

cube.parent = empty
$\endgroup$
5
  • 2
    $\begingroup$ Wow, that's a pretty impressive function, thanks. tbh I hadn't even thought of just parenting it but since I can't parent a material the same way I would have needed driver witchcraft and would have run into this problem which is pretty handily solved by your driver function, so thanks again. $\endgroup$ Commented Sep 26, 2015 at 7:18
  • $\begingroup$ @Technopath, I have rejected your edit for two reasons: 1. I tested the function and it works nicely in Blender 2.78, and: 2. The changes you've made mean that you always drive one object's property by the same property of another object - but the original design was to allow driving any property by any other property. $\endgroup$
    – TLousky
    Commented May 28, 2017 at 7:30
  • $\begingroup$ @TLousky Does this Python function work for Shapekeys as well? $\endgroup$ Commented May 28, 2018 at 0:02
  • $\begingroup$ This is important for scaling Audio Animation FCurve values that's been baked to the empty to some other geometry. $\endgroup$ Commented Sep 25, 2021 at 11:43
  • $\begingroup$ Is it not possible to directly use bpy.ops.ui.copy_as_driver_button() and bpy.ops.anim.paste_driver_button()? blender.stackexchange.com/questions/181797/… $\endgroup$
    – Harry McKenzie
    Commented Apr 18, 2023 at 1:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .