2
$\begingroup$

I'm using a python script to generate mesh geometry and to add multiple shape keys in Blender using bmesh. The goal is to have 10 shape keys and each generated vertex is modified by just one of those shape keys.

My first attempt was as follows:

import bpy
import bmesh

# Create mesh.
me = bpy.data.meshes.new('TestMesh')

# Create object.
ob = bpy.data.objects.new('TestObject',me)

# Bmesh.
bm = bmesh.new()

# Add default shape key.
ob.shape_key_add('Basis')

# List of shape keys to loop through after creation.
ShapeKeys = []

# Base name for shape keys.
KeyBaseStr = 'Shape'

# Create 20 vertices.
for iVert in range(1,21):

    # Index of shape key to add the vertex to.
    iGroup = (iVert-1) % 10

    # Check if group shape key has already been created.
    if len(ShapeKeys) < (iGroup + 1):

        # Name of new shape key.
        SetName = KeyBaseStr + '_Set_' + str(iVert)

        # Create shape key and append to list.
        ShapeKeys.append(bm.verts.layers.shape.new(SetName))

        # Shorthand for new key.
        sk = ShapeKeys[-1]

        print('created shape key #',len(ShapeKeys))
    else:
        # Select existing shape key.
        sk = ShapeKeys[iGroup]

        print('using existing shape key #',iGroup+1)

    # Add vertex.
    bv = bm.verts.new(tuple([iVert, 0, 1]))

    # Assign vertex coordinates in selected shape key.
    bv[sk] = tuple([0, iVert, 2])

# Bind bmesh to mesh.
bm.to_mesh(me)  

# Link object to scene.
scene = bpy.context.scene
scene.objects.link(ob)

# Set as active and selected.
scene.objects.active = ob
ob.select = True

However, running the code results in an object with just one shape key rather than 10. All ten still appear to be created according to what gets printed and the ShapeKey list indeed seems to hold 10 BMLayerItem objects, but seem to not get binded to the mesh or the object. I also tried the same approach but created all of the shape keys layers before any vertices, but it made no difference.

The question is simple, how to add multiple shape keys while also generating geometry one vertex at a time, preferably using bmesh?

Also asked this question on BlenderArtists but received no answers so far:

https://blenderartists.org/t/problem-adding-multiple-shape-keys-with-bmesh/1122506

$\endgroup$
  • $\begingroup$ Not sure there is any benefit in adding a new shapkey for a new vertex. The shapekey basically stores a location for all verts in the mesh. Would use a create mesh, create and manipulate shapekey data approach.. Example here $\endgroup$ – batFINGER Aug 22 '18 at 13:27
  • $\begingroup$ I see your point in general, but in the real application there will be 10,000 to 100,000 vertices and still just 10 shape keys. Thus making the vertex-shape key ratio a lot larger than the 2:1 in this simplified example. $\endgroup$ – akerblom Aug 23 '18 at 12:51
  • $\begingroup$ Not sure I see your point. For that many verts code above will be testing 100,000 times to see if there are 10 vertex groups that are created with the first 10 iterations? Added answer based on example posted in comment above. $\endgroup$ – batFINGER Aug 23 '18 at 18:44
  • $\begingroup$ I thought your problem was with the inefficiency of having so many shape keys compared to the number of vertices, but yes, it is certainly not the most efficient to check if a shape key exists inside the for-loop. However as stated in the question, the bmesh approach had the same result even when all the shape keys were created before the vertex creation loop. I'm very interested as to what goes wrong when using bmesh to create the shape keys. $\endgroup$ – akerblom Aug 27 '18 at 6:07
1
$\begingroup$

Alternative Route

  • Not using bmesh verts shape layers.

  • Created verts with a list comprehension, then reiterated over verts using the vertex index to index which shapekey layer data.

  • Pre-made the 10 shapekeys. Each key is a data layer on the verts that holds the shapekey location. The default if not set is the

Test script.

import bpy
import bmesh
from math import ceil
from bpy import context
N = 20 # total number of verts
g = 10 # verts per sk

scene = context.scene
me = bpy.data.meshes.new("Squircle")
obj = bpy.data.objects.new("Squircle", me)

bm = bmesh.new()
verts = [bm.verts.new((i, 0, 1)) for i in range(N)]
sk = obj.shape_key_add("Basis") 
# make shapekeys
sks = [obj.shape_key_add("SK_%02d" % i) for i in range(g)]

bm.to_mesh(me)

for v in bm.verts:
    sks[v.index % g].data[v.index].co = (0, v.index, 2)
scene.objects.link(obj)
$\endgroup$
  • $\begingroup$ This suggestion solves my problem and is thus marked as accepted. However, it does not use bmesh as requested. Still much appreciated! $\endgroup$ – akerblom Aug 27 '18 at 6:09

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.