I want to use the Python API to create a sequence of absolute shape keys that deform a mesh over time, however I'm having a lot of trouble figuring out the proper way to do that.
Currently I'm trying to mimic how I would do it by hand, i.e. using bpy.ops
, which I'm sure isn't the best way to begin with, but even then I can't get it to work. A minimal sample of what I'm trying to do:
import bpy
if __name__ == '__main__':
# Create nx*ny points as a starting mesh
nx = 11
ny = 11
data = [(x/10., y/10., (x**2+y**2)/200.) \
for x in range(nx) for y in range(ny)]
# Create edge loops to define faces
faces = []
count = 0
for i in range(ny*(nx-1)):
if count < ny-1:
faces.append((i, i+1, i+ny+1, i+ny))
count += 1
else:
count = 0
# Create mesh and object
mesh = bpy.data.meshes.new('mesh')
obj = bpy.data.objects.new('obj', mesh)
obj.location = (0, 0, 0)
bpy.context.scene.objects.link(obj)
mesh.from_pydata(data, [], faces)
mesh.update(calc_edges = True)
verts = obj.data.vertices
# Create a base shape key. This much seems to work.
bpy.context.scene.objects.active = obj
bpy.ops.object.shape_key_add()
bpy.ops.object.shape_key_retime()
obj.data.shape_keys.use_relative = False
skeys = obj.data.shape_keys
skbs = skeys.key_blocks
skbs[obj.active_shape_key_index].interpolation = 'KEY_LINEAR'
# Create 10 sequential deformations
for n in range(10):
# Create new shape key
bpy.ops.object.shape_key_add()
skbs[obj.active_shape_key_index].interpolation = 'KEY_LINEAR'
bpy.ops.object.shape_key_move(type='BOTTOM')
bpy.ops.object.mode_set(mode='EDIT')
# Deform the mesh
for i in range(len(verts)):
verts[i].co.z *= data[i][2]
mesh.update(calc_edges=True)
bpy.ops.object.mode_set(mode='OBJECT')
This does create the shape keys and put them in the correct order, but the mesh deformations aren't linked to the keys (because I'm modifying the vertices directly instead of using the edit-mode tools?), so the mesh always resembles the initial data. If I manually add another shape key after running this script, it magically takes the geometry of the last iteration, but the ones in between still don't seem to do anything.
What is the proper way to implement this (ideally without as much reliance on bpy.ops
)?