Drivers trigger once

Hi,

here I have simple setup (see attachment), object dimension property controlled by driver’s “Distance” variable, which measures distance between Empty and object itself.

I want that driver’s value could only grow up, but never go down, in order to achieve trigger effect like on this GIF animation with cube in the middle:

http://i.imgur.com/Kv2Lwbj.gif

Attachments

trigger.blend (350 KB)

1 Like

You should be able to do this with a driver function and a custom property.
Youll have to run the script and may have to update dependencies before the driver works.

Ive attached a modified example.
It was written for 2.70a, so it may need to be modified.

import bpy

def driverFunc(var):

    ob = bpy.context.scene.objects['Cube.001']
    if var > ob['prop']:
        ob['prop'] = var
    
    return ob['prop']

bpy.app.driver_namespace['driverFunc'] = driverFunc

Some more info here.
http://wiki.blender.org/index.php/Doc:2.6/Manual/Animation/Basics/Drivers#Driver_Namespace

Attachments

trigger2.blend (335 KB)

Or you can just swap out one object for another. After the Empty passed by and your animation is done animate the hide_render icon (Camera) in the Outliner to turn off. So you would need a duplicate of the object, without the driver, that you would animate on (in the same location) at the same time you animate off the object with the driver. The old slight-of-hand trick. No code needed but code can work too.

dirty-stick
I think this may be just what I need, but I have two questions which are not described in documentation:

  • Every time I’m trying to change a function name, I get undefined function error — is that means I can only have one custom driver function?
  • Your example shows how to handle one object, but how can this be applied to a hundred?

Atom
In this case it’s much easier to use fcurves and NLA strips, like I did in the example file.

dirty-stick
Thank you so much! Though it doesn’t work perfectly, it still works!
To make it more user friendly I wrote few more operators (place obj name into expression), few workarounds (“Update Dependencies” is not accessible from python) to make things automatic.

Handlers examples in official docs are so generic, that I’m not sure how to apply them in my case.

Node based animation system is the cure to my problem indeed, but there are so many of them, and not a single one with official support (which means they all die as soon as official implementation arrives), so I’d rather wait till Blender officials made their opinion on node based systems, and invest my time into learning Blender API which will benefit me in the long run.

Ive attached a .app.handler example.
Does the same thing, just easier to setup.
It should work after pressing run script, no drivers required.

Its best to clear .app.handlers when not using them by the way.
To clear, add a # in front of ‘bpy.app.handlers.scene_update_pre.append(app_handler)’, then run the script again.

.app.handler code.

import bpy, math

def dist3(x1,y1,z1,x2,y2,z2):
    return math.sqrt((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)

obs = []

obs.append(['Cube',0.5])
obs.append(['Cube.001',0.5])
obs.append(['Cube.002',0.5])

def app_handler(scene):

    em = bpy.context.scene.objects['Empty']

    for ob in obs:
        
        obj = bpy.context.scene.objects[ob[0]]
        dist = dist3(em.location.x,em.location.y,em.location.z,obj.location.x,obj.location.y,obj.location.z)

        ob[1] = 4-dist/2

        if ob[1] > obj.dimensions[0]:
            obj.dimensions[0] = ob[1]

# scene update
bpy.app.handlers.scene_update_pre.clear()
bpy.app.handlers.scene_update_pre.append(app_handler)

# render update
#bpy.app.handlers.render_pre.clear()
#bpy.app.handlers.render_pre.append(app_handler)

There is some info on .app.handlers here.
http://www.blender.org/documentation/blender_python_api_2_72_release/bpy.app.handlers.html

Attachments

trigger4.blend (310 KB)

Wow, thanks! But I can’t say it’s a better way than driver function for two reasons:

  • Lack of artistic control — it’s fine if I want to use linear interpolation when animating dimensions property, but if I want to use Ease In Out with with Exponential or Cubic Interpolation, or Stepped interpolation? Even if I know the formula, it’s still much more complicated than drivers.
  • Performance — three objects are fine, but when I’m trying to make it work with more than hundred, even if I’m not touching anything it’s affecting CPU performance really heavy, and never stops till I clear the handle, which is not acceptable at all.

But thanks to you now I have my set of animation tools that already helped me to finish my projects, plus now I have a better understanding of Blender API and Python.

Referencing objects by the context is not recommended try using the provided scene instead. If the context ends up blank or None while rendering your code will fail.

Change.

em = bpy.context.scene.objects['Empty']

To…


em = scene.objects.get('Empty')
if em != None:
    #Process code here.

Also test if the returned object is None before proceeding.

You mean change to?


bpy.data.objects

Because as I understand scene exists only with in context. Anyway it won’t help to make handlers more efficient.

hi again.

Atoms referring to the handler function.
The app_handler(scene): has a scene pointer, which is derived from bpy.context.scene, I think.

  1. The handlers like frame_change_post and frame_change_pre are more cpu friendly.

If using the driver function method, I dont think youll have to use custom properties.
You should be able to just update the dimension if the var is > dimensions[0].

import bpy

def driverFunc(var, name):

    dm = bpy.context.scene.objects[name].dimensions[0]
    if var > dm:
        dm = var
    
    return dm

bpy.app.driver_namespace['driverFunc'] = driverFunc

I couldnt find an operator to update dependencies via python.
There are some operations that trigger an update, modifying the driver expression via python for example.

If using the driver function method, I dont think youll have to use custom properties.

Already did that ^^

There are some operations that trigger an update, modifying the driver expression via python for example.

This exactly what I’m doing, couldn’t find any kind of operator:


objs = context.scene.objects
for obj in objs:
    if (obj.data and obj.data.shape_keys and
                     obj.data.shape_keys.animation_data and
                     obj.data.shape_keys.animation_data.drivers):
        fcus = obj.data.shape_keys.animation_data.drivers
        for fcu in fcus:
            if fcu.data_path == 'eval_time':
                fcu.driver.expression = fcu.driver.expression