Here is a simple demo script for keyframes on shaders and modifiers for multiple objects. In the future there will be much more going on, so I want to know if there is an easier way to do these kinds of things, or is all this scripting pretty much the right approach?
For example, near the end: ee = cube.active_material.node_tree.nodes.get("Emission")
what would I do if there were more than one Emission node? Can I name them? (there could be 100 materials) I wouldn't want to have to keep track of the order they are added if I can help it.
This is what this sample script does:
Scripting the nodes is modified from @zeffii here
def make_a_glo_mat(color_diff=None, color_emit=None,
emit_strength=None, frac=None):
# modified from HERE: https://blender.stackexchange.com/a/32162/5334
glo_mat = bpy.data.materials.new('Glow_mat')
glo_mat.use_nodes = True
nodes = glo_mat.node_tree.nodes
# material output and diffuse nodes are present already, get them by name
mat_output = nodes.get("Material Output")
diffuse = nodes.get("Diffuse BSDF") # new(type='ShaderNodeBsdfDiffuse')
mix = nodes.new(type='ShaderNodeMixShader')
emit = nodes.new(type='ShaderNodeEmission')
mat_output.location = 300, 0
mix.location = 0, 0
diffuse.location = -200, 50
emit.location = -200, -100
# link mix_shader to material output
a = mix.outputs['Shader']
b = mat_output.inputs['Surface']
glo_mat.node_tree.links.new(a, b)
# link diffuse to mix
a = diffuse.outputs['BSDF']
b = mix.inputs[1] # 'Shader' is used twice, so you access via index.
glo_mat.node_tree.links.new(a, b)
# link emissive to mix
a = emit.outputs['Emission']
b = mix.inputs[2]
glo_mat.node_tree.links.new(a, b)
if color_diff == None:
color_diff = [0.8, 0.0, 0.2, 1.0]
if color_emit == None:
color_emit = [0.2, 0.8, 0.8, 1.0]
if frac == None:
frac = 0.5
if emit_strength == None:
emit_strength = 0.5
mix.inputs[0].default_value = frac # factor
diffuse.inputs['Color'].default_value = color_diff
emit.inputs['Color'].default_value = color_emit
emit.inputs['Strength'].default_value = emit_strength
return glo_mat
This I wrote...
def make_funky_cube(radius=None, location=None):
if radius == None:
radius = 1.0
if location == None:
location = np.array([6,6,4])*(np.random.random(3)+np.array([-0.5, -0.5, +0.25]))
ok = bpy.ops.mesh.primitive_cube_add(radius=radius, location=location)
oo = bpy.ops.object
ao = bpy.context.active_object
oo.modifier_add(type='BEVEL')
ao.modifiers["Bevel"].width = 0.3
ao.modifiers["Bevel"].segments = 3
oo.shade_smooth()
return ao
And this now works thanks to this from @zeffii and this from @sambler .
import bpy
import numpy as np
scene = bpy.context.scene
scene.render.engine = 'CYCLES'
f_zero = np.array([[13, 22], [53, 62]])
sigma = np.array([[4, 10], [4, 10]])
n_frames = 80
# make materials in Nodes (black is blue for now)
glo_red_mat = make_a_glo_mat(color_diff=[0.8, 0.2, 0.2, 1.0],
color_emit=[1.0, 1.0, 1.0, 1.0],
emit_strength=1.0, frac=0.5)
glo_blk_mat = make_a_glo_mat(color_diff=[0.0, 0.0, 0.5, 1.0],
color_emit=[1.0, 1.0, 1.0, 1.0],
emit_strength=1.0, frac=0.5)
# make two cubes
red_cube = make_funky_cube(radius=1.0, location=(-2.0, 0.0 ,2.0))
black_cube = make_funky_cube(radius=1.0, location=(+2.0, 0.0, 2.0))
red_cube.active_material = glo_red_mat
black_cube.active_material = glo_blk_mat
bpy.context.scene.frame_end = n_frames
for i_frame in range(n_frames):
a = np.exp(-(i_frame - f_zero)**2 / (2.*sigma**2))
cubes = [red_cube, black_cube]
things = a[0], a[1]
for thing, cube in zip(things, cubes):
s = 1.00 + 0.40 * thing[0]
bw = 0.05 + 0.25 * thing[0]
e = 0.20 + 0.80 * thing[1]
# got help HERE: https://blender.stackexchange.com/a/10574/5334
cube.modifiers["Bevel"].width = bw
cube.scale = (s, s, s)
cube.keyframe_insert(data_path='scale', frame = i_frame + 1, index=-1)
cube.keyframe_insert(data_path='modifiers["Bevel"].width', frame = i_frame + 1, index=-1)
ee = cube.active_material.node_tree.nodes.get("Emission")
ee.inputs[1].default_value = e
ee.inputs[1].keyframe_insert(data_path="default_value", frame=i_frame+1)