1
$\begingroup$

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)?

$\endgroup$
1
$\begingroup$

You can create shapekeys directly as well as set the position of each vertex for each shapekey without using edit mode or even changing the active object. An object has a shape_key_add method that you should use instead of the operator, one advantage of the object method is that it returns the shapekey it creates, so you don't have to use an index into the array or set the active shapekey, you just work with each shapekey you create.

Each shapekey block has a data property that holds the location of each vertex in the object.

So after creating the mesh object -

verts = obj.data.vertices

sk_basis = obj.shape_key_add('Basis')
sk_basis.interpolation = 'KEY_LINEAR'
obj.data.shape_keys.use_relative = False

# Create 10 sequential deformations
for n in range(10):
    # Create new shape key
    sk = obj.shape_key_add('Deform')
    sk.interpolation = 'KEY_LINEAR'

    # position each vert
    for i in range(len(verts)):
        sk.data[i].co.z *= data[i][2]
$\endgroup$
  • $\begingroup$ Well, I feel like an idiot for missing that function. Unfortunately, though, the same problem still exists: the mesh does not deform when I change the evaluation time. When I manually add a new shape key, the mesh deforms from the initial to final shapes between times 100 and 110, i.e. only the manually added shape key works properly. $\endgroup$ – Zombie Feynman Jun 28 '18 at 8:59
  • $\begingroup$ Turns out adding from_mix=False to shape_key_add() does the trick. Oh, and I had to swap the "create shape key" and "deform mesh" blocks. $\endgroup$ – Zombie Feynman Jul 11 '18 at 13:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.