3

I have written some python to create a set of absolute shape keys:

import bpy
import bmesh
import math

def vert1For(u, t):
    return [ 0, u, 0]

def vert2For(u, t, dTheta, z1):
    theta1 = dTheta * math.sin(t+u*0.4)
    return [ math.sin(theta1)*z1, u, math.cos(theta1)*z1]

def vert3For(u, t, dTheta, z2, thetaLag):
    theta2 = dTheta * math.sin(t+u*0.4-thetaLag)
    return [ math.sin(theta2)*z2, u, math.cos(theta2)*z2]

def makeMesh(name, nSegs, z1, z2, dTheta, thetaLag):
    mesh = bpy.data.meshes.new(name)
    verts = []
    faces = []
    for u in range(0,nSegs+1):
        v1=len(verts)
        verts.append( vert1For(u,0) )
        verts.append( vert2For(u,0,dTheta, z1) )
        verts.append( vert3For(u,0,dTheta, z2, thetaLag) )
        if (u>0):
            v2 = v1+1
            v3 = v1+2
            v4 = v1+3
            v5 = v1+4
            v6 = v1+5
            faces.append( [ v1, v4, v5, v2] )
            faces.append( [ v2, v5, v6, v3] )
    mesh.from_pydata(verts, [], faces)
    mesh.validate(True)
    mesh.show_normal_face = True

    return mesh

def addShapeKey(obj, i, nKeys, z1, z2, dTheta, thetaLag):
    kn = "phase %d"%i
    sk = obj.shape_key_add(kn)
#    sk.value = 0
#    sk.frame = i/nKeys
#    sk.frame = i*i/(nKeys*nKeys) # crazy version
    bm = bmesh.new()
    bm.from_mesh(obj.data)
    sl = bm.verts.layers.shape.get(kn)


    for u in range( math.floor(len(bm.verts) / 3)):
        t = math.pi*2*i/nKeys
        bm.verts[u*3][sl] = vert1For(u, t)
        bm.verts[u*3+1][sl] = vert2For(u, t, dTheta, z1)
        bm.verts[u*3+2][sl] = vert3For(u, t, dTheta, z2, thetaLag)

    bm.to_mesh(obj.data)



dTheta = 0.8
thetaLag = 0.2
z1 = 2
z2 = 3
mesh = makeMesh("fin", 40, z1, z2, dTheta, thetaLag)

obj = bpy.data.objects.new("fin", mesh)
bpy.context.scene.objects.link(obj)


sk0 = obj.shape_key_add("Basis")
print(sk0)
sk0 = obj.data.shape_keys
print(sk0)
sk0.use_relative = False
nKeys = 11;
for i in range(1,nKeys):
    addShapeKey(obj, i, nKeys, z1, z2, dTheta, thetaLag)

# This next bit does not actually work, and I'm not sure why.  That's why pretty much everything in bpy.ops should be avoided.
obj.select = True
bpy.context.scene.objects.active = obj
print("retime")
bpy.ops.object.shape_key_retime()

Unfortunately, absolute shape keys are incomplete when you create them, whether from python or the UI. The only way to make them work is to give their secret .frame property a value, and since that secret property is read-only in python the only way I know to do that is to use the Reset Timing button to invoke bpy.ops.object.shape_key_retime().

Unfortunately, I do not know how to set the UI state such that a python call to bpy.ops.object.shape_key_retime() will work the same as clicking it from the UI. (and this is why I generally avoid bpy.ops like the plague)

What extra python calls are necessary to finalize the state of the object so that no extra user interaction is necessary? When I run the above code, the shape keys panel is even missing important bits of its UI until I click one of the shape keys in the list.

Mutant Bob
  • 9,243
  • 2
  • 29
  • 55

0 Answers0