Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
Version 0.1 for both curveAtFrame and wiggle functions.
  • Loading branch information
John Einselen committed May 5, 2021
1 parent f27fb5e commit 939295b
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 0 deletions.
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,46 @@
# VF-BlenderDriverFunctions
Functions for use in Blender channel drivers.

Installation and usage:
- Download the .py file
- Open up Blender preferences
- Install the add-on
- Enable
- Add a driver to any channel via keyboard shortcut (usually "D"), context menu (right-click), or directly (typing "#" and then the function)


## curveAtFrame
This driver function is intended to mimic Adobe After Effect's "valueAtTime" expression, returning the value of an animated channel from the specified point in time.

After Effects expression reference:
```javascript
thisComp.layer("Cube").transform.position.valueAtTime(time-0.167)[0]
```
This returns the value of the "Cube" layer's X position from 0.167 seconds in the past, relative to the current time.

Blender driver equivalent:
```javascript
curveAtFrame("Cube", 0, frame-5)
```
This returns the value of the "Cube" object's first animation curve from 5 frames in the past, relative to the current frame.

Note that Blender's time sampling doesn't allow references to an object's transform property; it has to be an animation curve, and it can only be referenced by index. The first channel that is keyframed will be assigned index 0, the second channel to be animated will be index 1, and so on.


## wiggle
Designed to mimic Adobe After Effect's "wiggle" expression with similar frequency (colloquially known as wiggles per second) and octave settings.

After Effects expression reference:
```javascript
seedRandom(4, true);
wiggle(3, 200, 1)
```
This automatically animates a channel using a static seed of 4 with about 3 "wiggles" per second, a potential distance range of -200 to 200 pixels, and 1 octave of noise.

Blender driver equivalent:
```javascript
wiggle(3, 0.2, 1, 4)
```
This automatically animates a channel and vaguely matches AE's 3 "wiggles" per second, a potential distance range of -200mm to 200mm (if used in a transform channel), with 1 octave of noise, and a random seed of 4 (seeds can be any floating point number, including negative numbers).

Note that unlike After Effects, driver functions in Blender don't automatically receive unique identifiers for each channel they are applied to, so a unique seed value must be provided by the user.
44 changes: 44 additions & 0 deletions VF_curveAtFrame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
bl_info = {
"name": "VF curveAtFrame",
"author": "John Einselen - Vectorform LLC",
"version": (0, 1),
"blender": (2, 80, 0),
"location": "Channel driver -> curveAtFrame(\"Cube\", 0, frame-5)",
"description": "Adds curveAtFrame(objectName, curveIndex, time) driver function",
"warning": "inexperienced developer, use at your own risk",
"wiki_url": "",
"tracker_url": "",
"category": "Rigging"}

# Thanks for the help:
# https://blenderartists.org/t/read-object-properties-from-a-specific-frame/531238/2
# https://blender.stackexchange.com/questions/71305/how-to-make-an-addon-with-custom-driver-function

# Example usage:
# curveAtFrame("Cube", 0, frame-5)
# returns the "Cube" object's first animation curve value 5 frames in the past
# note that Blender requires an animation curve to get time-based data, and doesn't reference them by type or name, only index number

import bpy
from bpy.app.handlers import persistent
from bpy.app import driver_namespace as dns

def curve_at_time(name, channel, frame):
obj = bpy.data.objects[name]
fcurve = obj.animation_data.action.fcurves[channel]
return fcurve.evaluate(frame)

@persistent
def load_handler(dummy):
dns = bpy.app.driver_namespace
dns["curveAtFrame"] = curve_at_time

def register():
load_handler(None)
bpy.app.handlers.load_post.append(load_handler)

def unregister():
bpy.app.handlers.load_post.remove(load_handler)

if __name__ == "__main__":
register()
43 changes: 43 additions & 0 deletions VF_wiggle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
bl_info = {
"name": "VF wiggle",
"author": "John Einselen - Vectorform LLC",
"version": (0, 1),
"blender": (2, 80, 0),
"location": "Channel driver -> wiggle(2, 0.5, 1, 2.5)",
"description": "Adds wiggle(frequency, amplitude, octaves, seed) driver function",
"warning": "inexperienced developer, use at your own risk",
"wiki_url": "",
"tracker_url": "",
"category": "Rigging"}

# Thanks for the help:
# https://blender.stackexchange.com/questions/71305/how-to-make-an-addon-with-custom-driver-function

# Example usage:
# wiggle(2, 2, 5)
# this is vaguely comparable to AE's 2 wiggles per second with 2 octaves and a random seed of 5

import bpy
from bpy.app.handlers import persistent
from bpy.app import driver_namespace as dns
from mathutils import noise

def wiggle(freq, amp, oct, seed):
time = bpy.context.scene.frame_current / bpy.context.scene.render.fps
pos = (time*0.73*freq, time*0.53*freq, seed) # magic numbers to try and mimic the actually-faster-than-per-second wiggle value in AE
return noise.fractal(pos, 1.0, 2.0, oct, noise_basis='PERLIN_ORIGINAL') * amp

@persistent
def load_handler(dummy):
dns = bpy.app.driver_namespace
dns["wiggle"] = wiggle

def register():
load_handler(None)
bpy.app.handlers.load_post.append(load_handler)

def unregister():
bpy.app.handlers.load_post.remove(load_handler)

if __name__ == "__main__":
register()

0 comments on commit 939295b

Please sign in to comment.