1
$\begingroup$

I want to apply all shape keys to selected objects, except the shape keys that have the word face in the name. How can I do that? I have this script that applies all shape keys to selected objects, but it does not leave the shape keys that have the word face in the name.

import bpy
scene = bpy.context.scene

for o in bpy.context.selected_objects:
    if o.type == "MESH":
        if o.data.shape_keys:
            for k in o.data.shape_keys.key_blocks:
                ojs = bpy.data.objects
                bpy.context.view_layer.objects.active = o #set context.object

                o.shape_key_add(name='CombinedKeys', from_mix=True)
                if o.data.shape_keys:
                    for shapeKey in o.data.shape_keys.key_blocks:
                        o.shape_key_remove(shapeKey)
$\endgroup$
2
  • $\begingroup$ Formatting note: I advise using triple ``` marks to format code instead of indenting the whole code, because almost every wrongly indented code that I have encountered on this website were formatted with indentation instead of triple ``` marks. Hence my edit of your question. $\endgroup$
    – Lauloque
    Commented Aug 13 at 15:10
  • 1
    $\begingroup$ Your current method first creates a new shapekey of all the combined shapes. 1) Does that mean you want to preserve the deleted shapekeys as a new shape? 2) Does this new combined shape needs to also include the shape key you want to keep? 3) Does this combinedkeys need to be preserved as well? It would be quite a different procedure depending on your answers here. $\endgroup$
    – Lauloque
    Commented Aug 13 at 15:15

1 Answer 1

1
$\begingroup$

Starting from this code:

import bpy
scene = bpy.context.scene

for o in bpy.context.selected_objects:
    if o.type == "MESH":
        if o.data.shape_keys:
            for k in o.data.shape_keys.key_blocks:
                ojs = bpy.data.objects
                bpy.context.view_layer.objects.active = o #set context.object

                o.shape_key_add(name='CombinedKeys', from_mix=True)
                if o.data.shape_keys:
                    for shapeKey in o.data.shape_keys.key_blocks:
                        o.shape_key_remove(shapeKey)

A few things could be tweaked:

Setting the active object

It isn't even needed for your script to work. But if you did something else that required it, it should definitely happen in the object loop, not in the keys loop. Otherwise, you would set the same object active multiple times for nothing.

For and If statement

Anytime you have a For followed by IF, you could simplify it. I.E:

for o in bpy.context.selected_objects:
    if o.type == "MESH":
        if o.data.shape_keys:

Can be simplified into:

for o in (o for o in bpy.context.selected_objects if o.type == 'MESH' and o.data.shape_keys):

Making a combined shapekey

For some reason, you make a new shape from mix. But your current code have this happen as many times as there are shape in your object. This sounds quite tedious. Plus, this function works with the "current visual state" of the mesh. Meaning, if you have all your shape keys influences at zero, the shape from mix will be exactly as your base shape.

This needs better thinking, what is your goal with that? To preserve all the additional shape keys as one shape? Does it need to include the shapes named as "face"? Does the script have to turn all shape keys influence to 1 first?

In addition, your current script deletes that shape key anyway, so it's effectively useless.

In my revision, I will assume you want to create a new mixed shapekey to combine all the ones we delete.

Since we will need to make operations on either filtered in or filtered out object alternatively, I will make a list of them instead of always making for loops with extra filters all the time. It will make the code more readable, easier to manage by having only one place to handle filtering, allows you to easily do more operation on either lists of objects, and also I think it would be more performant (I think, I'm no expert).

shapes_to_delete, shapes_to_preserve = [], []
for shape in o.data.shape_keys.key_blocks:(
    shapes_to_delete if 'face' not in shape.name.lower() and 'basis' not in shape.name.lower()
    else shapes_to_preserve
    ).append(shape)

Note that I included the "basis" shape in the list of shapes to preserve. Since this is essentially your "original" mesh on which any shapekey is usually based on, you really need to keep it to not break your object.

Also note that I matched the names with name.lower() instead of just name. The former allows to not worry about capitalization. If you want to have a case-sensitive matching, use name instead.

Final procedures:

All we need to do is:

  1. set all to-preserve keys' values to zero
  2. set all to-preserve keys' values to 1
  3. create new shape from mix
  4. add the new shape to list of to preserve (if you want to do more operation on them later on)
  5. delete all to-delete shapes

These can be done quite simply with a few For loops.

Final result:

import bpy

for o in (o for o in bpy.context.selected_objects if o.type == 'MESH' and o.data.shape_keys):
    print(f"Found mesh object with shapekey data: {o.name}")
#    bpy.context.view_layer.objects.active = o # not needed here, but just in case, that's where you'd put it.

    shapes_to_delete, shapes_to_preserve = [], []
    for shape in o.data.shape_keys.key_blocks:(
        shapes_to_delete if 'face' not in shape.name.lower() and 'basis' not in shape.name.lower()
        else shapes_to_preserve
        ).append(shape)
    
    print("  Keys to preserve:")
    for shape in shapes_to_preserve:
        print(f"    {shape.name} of value: {shape.value}")
        shape.value = 0
        
    print("  Keys to delete:")
    for shape in shapes_to_delete:
        print(f"    {shape.name}")
        shape.value = 1
    
    new_shape = o.shape_key_add(name='CombinedKeys', from_mix=True)
    shapes_to_preserve.append(new_shape)
    
    for k in shapes_to_delete:
        o.shape_key_remove(k)

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .